You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by an...@apache.org on 2018/02/15 17:09:19 UTC

[1/2] jclouds git commit: [JCLOUDS-1382] improve usage of Neutron capabilities from Nova

Repository: jclouds
Updated Branches:
  refs/heads/master 88c84af87 -> 50ae6828f


http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CleanupResources.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CleanupResources.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CleanupResources.java
index 5da0b87..3e215bb 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CleanupResources.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CleanupResources.java
@@ -68,6 +68,7 @@ public class CleanupResources implements Function<NodeMetadata, Boolean> {
 
    public boolean removeSecurityGroupCreatedByJcloudsAndInvalidateCache(Set<String> tags) {
       String securityGroupIdCreatedByJclouds = getSecurityGroupIdCreatedByJclouds(tags);
+      if (securityGroupIdCreatedByJclouds == null) return true;
       return securityGroupExtension.removeSecurityGroup(securityGroupIdCreatedByJclouds);
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/RemoveFloatingIpFromNodeAndDeallocate.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/RemoveFloatingIpFromNodeAndDeallocate.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/RemoveFloatingIpFromNodeAndDeallocate.java
index 94309c8..8161443 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/RemoveFloatingIpFromNodeAndDeallocate.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/RemoveFloatingIpFromNodeAndDeallocate.java
@@ -19,19 +19,23 @@ package org.jclouds.openstack.nova.v2_0.compute.functions;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import javax.annotation.Resource;
-import javax.inject.Inject;
 import javax.inject.Named;
 
+import com.google.common.base.Supplier;
+import com.google.inject.Inject;
+import org.jclouds.Context;
 import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.logging.Logger;
+import org.jclouds.openstack.neutron.v2.NeutronApi;
 import org.jclouds.openstack.nova.v2_0.NovaApi;
-import org.jclouds.openstack.nova.v2_0.domain.FloatingIP;
+import org.jclouds.openstack.nova.v2_0.domain.FloatingIpForServer;
 import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndId;
 import org.jclouds.openstack.nova.v2_0.extensions.FloatingIPApi;
 
 import com.google.common.base.Function;
 import com.google.common.base.MoreObjects;
 import com.google.common.cache.LoadingCache;
+import org.jclouds.rest.ApiContext;
 
 /**
  * A function for removing and deallocating an ip address from a node
@@ -42,31 +46,51 @@ public class RemoveFloatingIpFromNodeAndDeallocate implements Function<RegionAnd
    @Named(ComputeServiceConstants.COMPUTE_LOGGER)
    protected Logger logger = Logger.NULL;
 
+   @Inject(optional = true)
+   @Named("openstack-neutron")
+   private Supplier<Context> neutronContextSupplier;
+
    private final NovaApi novaApi;
-   private final LoadingCache<RegionAndId, Iterable<? extends FloatingIP>> floatingIpCache;
+   private final LoadingCache<RegionAndId, Iterable<? extends FloatingIpForServer>> floatingIpCache;
 
    @Inject
    public RemoveFloatingIpFromNodeAndDeallocate(NovaApi novaApi,
-            @Named("FLOATINGIP") LoadingCache<RegionAndId, Iterable<? extends FloatingIP>> floatingIpCache) {
+            @Named("FLOATINGIP") LoadingCache<RegionAndId, Iterable<? extends FloatingIpForServer>> floatingIpCache) {
       this.novaApi = checkNotNull(novaApi, "novaApi");
       this.floatingIpCache = checkNotNull(floatingIpCache, "floatingIpCache");
    }
 
    @Override
    public RegionAndId apply(RegionAndId id) {
-      FloatingIPApi floatingIpApi = novaApi.getFloatingIPApi(id.getRegion()).get();
-      for (FloatingIP ip : floatingIpCache.getUnchecked(id)) {
-         logger.debug(">> removing floatingIp(%s) from node(%s)", ip, id);
-         floatingIpApi.removeFromServer(ip.getIp(), id.getId());
-         logger.debug(">> deallocating floatingIp(%s)", ip);
-         floatingIpApi.delete(ip.getId());
+      if (isNeutronLinked()) {
+         for (FloatingIpForServer floatingIpForServer : floatingIpCache.getUnchecked(id)) {
+            logger.debug(">> deallocating floatingIp(%s)", floatingIpForServer);
+            getFloatingIPApi(id.getRegion()).delete(floatingIpForServer.floatingIpId());
+         }
+      } else { // try nova
+         FloatingIPApi floatingIpApi = novaApi.getFloatingIPApi(id.getRegion()).get();
+         for (FloatingIpForServer floatingIpForServer : floatingIpCache.getUnchecked(id)) {
+            logger.debug(">> removing floatingIp(%s) from node(%s)", floatingIpForServer, id);
+            floatingIpApi.removeFromServer(floatingIpForServer.ip(), id.getId());
+            logger.debug(">> deallocating floatingIp(%s)", floatingIpForServer);
+            floatingIpApi.delete(floatingIpForServer.floatingIpId());
+         }
       }
       floatingIpCache.invalidate(id);
       return id;
    }
 
+   // FIXME remove duplications from AllocateAndAddFloatingIpToNode
+   private boolean isNeutronLinked() {
+      return neutronContextSupplier != null && neutronContextSupplier.get() != null;
+   }
+
+   private org.jclouds.openstack.neutron.v2.features.FloatingIPApi getFloatingIPApi(String region) {
+      return ((ApiContext<NeutronApi>) neutronContextSupplier.get()).getApi().getFloatingIPApi(region);
+   }
+
    @Override
    public String toString() {
-      return MoreObjects.toStringHelper("RemoveFloatingIpFromNodeAndDecreate").toString();
+      return MoreObjects.toStringHelper("RemoveFloatingIpFromNodeAndDeallocate").toString();
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/loaders/LoadFloatingIpsForInstance.java
----------------------------------------------------------------------
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 1487a13..2e404d5 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
@@ -19,8 +19,10 @@ package org.jclouds.openstack.nova.v2_0.compute.loaders;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
+import com.google.common.base.Function;
 import org.jclouds.openstack.nova.v2_0.NovaApi;
 import org.jclouds.openstack.nova.v2_0.domain.FloatingIP;
+import org.jclouds.openstack.nova.v2_0.domain.FloatingIpForServer;
 import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndId;
 import org.jclouds.openstack.nova.v2_0.extensions.FloatingIPApi;
 
@@ -35,7 +37,7 @@ import com.google.common.collect.ImmutableSet;
  * them.
  */
 @Singleton
-public class LoadFloatingIpsForInstance extends CacheLoader<RegionAndId, Iterable<? extends FloatingIP>> {
+public class LoadFloatingIpsForInstance extends CacheLoader<RegionAndId, Iterable<? extends FloatingIpForServer>> {
    private final NovaApi api;
 
    @Inject
@@ -44,7 +46,7 @@ public class LoadFloatingIpsForInstance extends CacheLoader<RegionAndId, Iterabl
    }
 
    @Override
-   public Iterable<? extends FloatingIP> load(final RegionAndId key) throws Exception {
+   public Iterable<? extends FloatingIpForServer> load(final RegionAndId key) throws Exception {
       String region = key.getRegion();
       Optional<? extends FloatingIPApi> ipApiOptional = api.getFloatingIPApi(region);
       if (ipApiOptional.isPresent()) {
@@ -54,7 +56,13 @@ public class LoadFloatingIpsForInstance extends CacheLoader<RegionAndId, Iterabl
                      public boolean apply(FloatingIP input) {
                         return key.getId().equals(input.getInstanceId());
                      }
-                  });
+                  })
+                 .transform(new Function<FloatingIP, FloatingIpForServer>() {
+                     @Override
+                     public FloatingIpForServer apply(FloatingIP input) {
+                         return FloatingIpForServer.create(key, input.getId(), input.getIp());
+                     }
+                 });
       }
       return ImmutableSet.of();
    }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/FloatingIpForServer.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/FloatingIpForServer.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/FloatingIpForServer.java
new file mode 100644
index 0000000..784ebfc
--- /dev/null
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/FloatingIpForServer.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.nova.v2_0.domain;
+
+import com.google.auto.value.AutoValue;
+import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndId;
+
+@AutoValue
+public abstract class FloatingIpForServer {
+
+   public abstract RegionAndId serverId();
+   public abstract String floatingIpId();
+   public abstract String ip();
+
+   public static FloatingIpForServer create(RegionAndId serverId,
+                               String floatingIpId,
+                               String ip
+   ) {
+      return new AutoValue_FloatingIpForServer(serverId, floatingIpId, ip);
+   }
+
+   FloatingIpForServer() {
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/PortInterface.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/PortInterface.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/PortInterface.java
new file mode 100644
index 0000000..4931016
--- /dev/null
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/PortInterface.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.nova.v2_0.domain;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Enums;
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+import org.jclouds.json.SerializedNames;
+
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+@AutoValue
+public abstract class PortInterface {
+
+    public enum State {
+        PROVISIONING, QUEUED, ACTIVE;
+
+        public static State fromValue(String value) {
+            Optional<State> state = Enums.getIfPresent(State.class, value.toUpperCase());
+            checkArgument(state.isPresent(), "Expected one of %s but was %s", Joiner.on(',').join(State.values()), value);
+            return state.get();
+        }
+    }
+
+    public abstract String portId();
+    public abstract String netId();
+    public abstract State portState();
+    public abstract List<FixedIP> fixedIPS();
+
+    @SerializedNames({"port_id", "net_id", "port_state", "fixed_ips"})
+    public static PortInterface create(String portId, String netId, State portState, List<FixedIP> fixedIPS) {
+        return new AutoValue_PortInterface(portId, netId, portState,
+                fixedIPS == null ? ImmutableList.<FixedIP> of() : ImmutableList.copyOf(fixedIPS)
+        );
+    }
+
+    PortInterface() {}
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ServerApi.java
----------------------------------------------------------------------
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 512f4a0..555d4e4 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
@@ -42,6 +42,7 @@ import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
 import org.jclouds.collect.PagedIterable;
 import org.jclouds.fallbacks.MapHttp4xxCodesToExceptions;
 import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.openstack.nova.v2_0.domain.PortInterface;
 import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup;
 import org.jclouds.openstack.v2_0.domain.PaginatedCollection;
 import org.jclouds.openstack.keystone.auth.filters.AuthenticateRequest;
@@ -446,4 +447,18 @@ public interface ServerApi {
    @Fallback(Fallbacks.EmptySetOnNotFoundOr404.class)
    Set<SecurityGroup> listSecurityGroupForServer(@PathParam("id") String id);
 
+   /**
+    * Lists port interfaces that are attached to a server.
+    *
+    * @param id
+    *           id of the server
+    * @return a list of ports attached to the server
+    */
+   @Named("server:getPortInterfaces")
+   @GET
+   @Path("/{id}/os-interface")
+   @SelectJson("interfaceAttachments")
+   @Fallback(Fallbacks.EmptySetOnNotFoundOr404.class)
+   Set<PortInterface> listPortInterfaces(@PathParam("id") String id);
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceLiveTest.java
index 1b6c6ad..d4aac07 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceLiveTest.java
@@ -21,15 +21,12 @@ import static java.util.logging.Logger.getAnonymousLogger;
 import java.util.Properties;
 
 import org.jclouds.compute.internal.BaseComputeServiceLiveTest;
-import org.jclouds.logging.config.LoggingModule;
-import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
 import org.jclouds.openstack.keystone.config.KeystoneProperties;
 import org.jclouds.openstack.nova.v2_0.config.NovaProperties;
 import org.jclouds.rest.AuthorizationException;
 import org.jclouds.sshj.config.SshjSshClientModule;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.ImmutableSet;
 import com.google.inject.Module;
 
 @Test(groups = "live", singleThreaded = true, testName = "NovaComputeServiceLiveTest")
@@ -43,17 +40,7 @@ public class NovaComputeServiceLiveTest extends BaseComputeServiceLiveTest {
    protected Module getSshModule() {
       return new SshjSshClientModule();
    }
-
-   @Override
-   protected LoggingModule getLoggingModule() {
-      return new SLF4JLoggingModule();
-   }
-
-   @Override
-   protected Iterable<Module> setupModules() {
-      return ImmutableSet.of(getLoggingModule(), credentialStoreModule, getSshModule());
-   }
-
+   
    @Override
    public void testOptionToNotBlock() {
       // start call is blocking anyway.
@@ -62,14 +49,12 @@ public class NovaComputeServiceLiveTest extends BaseComputeServiceLiveTest {
    @Test(enabled = true, dependsOnMethods = "testReboot")
    public void testSuspendResume() throws Exception {
       try {
-         // may fail because of lack of AdminActions extension or non-admin user, so log
-         // and continue
+         // may fail because of lack of AdminActions extension or non-admin user, so log and continue
          super.testSuspendResume();
       } catch (AuthorizationException e) {
          getAnonymousLogger().info("testSuspendResume() threw, probably due to lack of privileges: " + e.getMessage());
       } catch (UnsupportedOperationException e) {
-         getAnonymousLogger().info(
-               "testSuspendResume() threw, probably due to unavailable AdminActions extension: " + e.getMessage());
+         getAnonymousLogger().info("testSuspendResume() threw, probably due to unavailable AdminActions extension: " + e.getMessage());
       }
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaWithNeutronComputeServiceLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaWithNeutronComputeServiceLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaWithNeutronComputeServiceLiveTest.java
new file mode 100644
index 0000000..1559910
--- /dev/null
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaWithNeutronComputeServiceLiveTest.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.nova.v2_0.compute;
+
+import static java.util.logging.Logger.getAnonymousLogger;
+
+import java.util.Properties;
+
+import com.google.common.collect.ImmutableSet;
+import org.jclouds.Context;
+import org.jclouds.ContextBuilder;
+import org.jclouds.compute.internal.BaseComputeServiceLiveTest;
+import org.jclouds.config.ContextLinking;
+import org.jclouds.encryption.bouncycastle.config.BouncyCastleCryptoModule;
+import org.jclouds.logging.config.LoggingModule;
+import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
+import org.jclouds.openstack.keystone.config.KeystoneProperties;
+import org.jclouds.openstack.nova.v2_0.config.NovaProperties;
+import org.jclouds.rest.AuthorizationException;
+import org.jclouds.sshj.config.SshjSshClientModule;
+import org.testng.annotations.Test;
+
+import com.google.inject.Module;
+
+@Test(groups = "live", singleThreaded = true, testName = "NovaWithNeutronComputeServiceLiveTest")
+public class NovaWithNeutronComputeServiceLiveTest extends BaseComputeServiceLiveTest {
+
+   private Context neutronApiContext;
+
+   public NovaWithNeutronComputeServiceLiveTest() {
+      provider = "openstack-nova";
+
+      Properties overrides = setupProperties();
+      neutronApiContext = ContextBuilder.newBuilder("openstack-neutron")
+              .endpoint(setIfTestSystemPropertyPresent(overrides,
+                      "openstack-nova.endpoint"))
+              .credentials(setIfTestSystemPropertyPresent(overrides,
+                      "openstack-nova.identity"),
+                      setIfTestSystemPropertyPresent(overrides, "openstack-nova.credential"))
+              .modules(ImmutableSet.<Module>of(
+                      new SshjSshClientModule(),
+                      new SLF4JLoggingModule(),
+                      new BouncyCastleCryptoModule())
+              )
+              .build();
+   }
+
+   @Override
+   protected Module getSshModule() {
+      return new SshjSshClientModule();
+   }
+
+   @Override
+   protected LoggingModule getLoggingModule() {
+      return new SLF4JLoggingModule();
+   }
+
+   @Override
+   protected Iterable<Module> setupModules() {
+      return ImmutableSet.of(
+              ContextLinking.linkContext(neutronApiContext),
+              getLoggingModule(),
+              credentialStoreModule,
+              getSshModule()
+      );
+   }
+   
+   @Override
+   public void testOptionToNotBlock() {
+      // start call is blocking anyway.
+   }
+
+   @Test(enabled = true, dependsOnMethods = "testReboot")
+   public void testSuspendResume() throws Exception {
+      try {
+         // may fail because of lack of AdminActions extension or non-admin user, so log and continue
+         super.testSuspendResume();
+      } catch (AuthorizationException e) {
+         getAnonymousLogger().info("testSuspendResume() threw, probably due to lack of privileges: " + e.getMessage());
+      } catch (UnsupportedOperationException e) {
+         getAnonymousLogger().info("testSuspendResume() threw, probably due to unavailable AdminActions extension: " + e.getMessage());
+      }
+   }
+
+   @Test(enabled = true, dependsOnMethods = "testSuspendResume")
+   @Override
+   public void testGetNodesWithDetails() throws Exception {
+      super.testGetNodesWithDetails();
+   }
+
+   @Test(enabled = true, dependsOnMethods = "testSuspendResume")
+   @Override
+   public void testListNodes() throws Exception {
+      super.testListNodes();
+   }
+
+   @Test(enabled = true, dependsOnMethods = "testSuspendResume")
+   @Override
+   public void testListNodesByIds() throws Exception {
+      super.testListNodesByIds();
+   }
+
+   @Test(enabled = true, dependsOnMethods = { "testListNodes", "testGetNodesWithDetails", "testListNodesByIds" })
+   @Override
+   public void testDestroyNodes() {
+      super.testDestroyNodes();
+   }
+
+   @Override
+   protected Properties setupProperties() {
+      Properties props = super.setupProperties();
+      setIfTestSystemPropertyPresent(props, KeystoneProperties.CREDENTIAL_TYPE);
+      setIfTestSystemPropertyPresent(props, NovaProperties.AUTO_ALLOCATE_FLOATING_IPS);
+      return props;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/AllocateAndAddFloatingIpToNodeExpectTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/AllocateAndAddFloatingIpToNodeExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/AllocateAndAddFloatingIpToNodeExpectTest.java
index af20d85..70ef379 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/AllocateAndAddFloatingIpToNodeExpectTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/AllocateAndAddFloatingIpToNodeExpectTest.java
@@ -68,14 +68,27 @@ public class AllocateAndAddFloatingIpToNodeExpectTest extends BaseNovaComputeSer
       HttpResponse createFloatingIPResponse = HttpResponse.builder().statusCode(200).payload(
                payloadFromResource("/floatingip_details.json")).build();
 
+      HttpRequest listAZs = HttpRequest.builder().method("GET").endpoint(
+              URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v2/3456/os-availability-zone")).headers(
+              ImmutableMultimap.<String, String> builder().put("Accept", "application/json")
+                      .put("X-Auth-Token", authToken)
+                      .build())
+              .build();
+
+      HttpResponse listAZsResponseForUnassigned = HttpResponse.builder().statusCode(200).payload(
+              payloadFromResource("/availability_zone_list.json")).build();
+
       HttpRequest addFloatingIPRequest = addFloatingIPForAddress("10.0.0.3");
 
       AllocateAndAddFloatingIpToNode fn = requestsSendResponses(
-               ImmutableMap.<HttpRequest, HttpResponse> builder().put(keystoneAuthWithUsernameAndPasswordAndTenantName,
-                        responseWithKeystoneAccess).put(extensionsOfNovaRequest, extensionsOfNovaResponse).put(
-                        createFloatingIP, createFloatingIPResponse)
-                        .put(addFloatingIPRequest, addFloatingIPResponse).build()).getContext().utils().injector()
-               .getInstance(AllocateAndAddFloatingIpToNode.class);
+               ImmutableMap.<HttpRequest, HttpResponse> builder()
+                       .put(keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess)
+                       .put(listAZs, listAZsResponseForUnassigned)
+                       .put(extensionsOfNovaRequest, extensionsOfNovaResponse)
+                       .put(createFloatingIP, createFloatingIPResponse)
+                       .put(addFloatingIPRequest, addFloatingIPResponse).build())
+              .getContext().utils().injector()
+              .getInstance(AllocateAndAddFloatingIpToNode.class);
 
       AtomicReference<NodeMetadata> nodeRef = Atomics.newReference(node);
       AtomicReference<NovaTemplateOptions> optionsRef = Atomics.newReference(options);
@@ -110,6 +123,16 @@ public class AllocateAndAddFloatingIpToNodeExpectTest extends BaseNovaComputeSer
                                  "{\"badRequest\": {\"message\": \"AddressLimitExceeded: Address quota exceeded. You cannot create any more addresses\", \"code\": 400}}",
                                  "application/json")).build();
 
+      HttpRequest listAZs = HttpRequest.builder().method("GET").endpoint(
+              URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v2/3456/os-availability-zone")).headers(
+              ImmutableMultimap.<String, String> builder().put("Accept", "application/json")
+                      .put("X-Auth-Token", authToken)
+                      .build())
+              .build();
+
+      HttpResponse listAZsResponseForUnassigned = HttpResponse.builder().statusCode(200).payload(
+              payloadFromResource("/availability_zone_list.json")).build();
+
       HttpRequest list = HttpRequest.builder().method("GET").endpoint(
                URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v2/3456/os-floating-ips")).headers(
                ImmutableMultimap.<String, String> builder().put("Accept", "application/json").put("X-Auth-Token",
@@ -121,12 +144,16 @@ public class AllocateAndAddFloatingIpToNodeExpectTest extends BaseNovaComputeSer
       HttpRequest addFloatingIPRequest = addFloatingIPForAddress("10.0.0.5");
 
       AllocateAndAddFloatingIpToNode fn = requestsSendResponses(
-               ImmutableMap.<HttpRequest, HttpResponse> builder().put(keystoneAuthWithUsernameAndPasswordAndTenantName,
-                        responseWithKeystoneAccess).put(extensionsOfNovaRequest, extensionsOfNovaResponse).put(
-                        createFloatingIP, createFloatingIPResponse)
-                        .put(addFloatingIPRequest, addFloatingIPResponse).put(list,
-                                 listResponseForUnassigned).build()).getContext().utils().injector()
-               .getInstance(AllocateAndAddFloatingIpToNode.class);
+               ImmutableMap.<HttpRequest, HttpResponse> builder()
+                       .put(keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess)
+                       .put(listAZs, listAZsResponseForUnassigned)
+                       .put(extensionsOfNovaRequest, extensionsOfNovaResponse)
+                       .put(createFloatingIP, createFloatingIPResponse)
+                       .put(addFloatingIPRequest, addFloatingIPResponse)
+                       .put(list, listResponseForUnassigned)
+                       .build())
+              .getContext().utils().injector()
+              .getInstance(AllocateAndAddFloatingIpToNode.class);
 
       AtomicReference<NodeMetadata> nodeRef = Atomics.newReference(node);
       AtomicReference<NovaTemplateOptions> optionsRef = Atomics.newReference(options);
@@ -148,6 +175,16 @@ public class AllocateAndAddFloatingIpToNodeExpectTest extends BaseNovaComputeSer
                                  "{\"badRequest\": {\"message\": \"AddressLimitExceeded: Address quota exceeded. You cannot create any more addresses\", \"code\": 404}}",
                                  "application/json")).build();
 
+      HttpRequest listAZs = HttpRequest.builder().method("GET").endpoint(
+              URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v2/3456/os-availability-zone")).headers(
+              ImmutableMultimap.<String, String> builder().put("Accept", "application/json")
+                      .put("X-Auth-Token", authToken)
+                      .build())
+              .build();
+
+      HttpResponse listAZsResponseForUnassigned = HttpResponse.builder().statusCode(200).payload(
+              payloadFromResource("/availability_zone_list.json")).build();
+
       HttpRequest list = HttpRequest.builder().method("GET").endpoint(
                URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v2/3456/os-floating-ips")).headers(
                ImmutableMultimap.<String, String> builder().put("Accept", "application/json").put("X-Auth-Token",
@@ -159,12 +196,15 @@ public class AllocateAndAddFloatingIpToNodeExpectTest extends BaseNovaComputeSer
       HttpRequest addFloatingIPRequest = addFloatingIPForAddress("10.0.0.5");
 
       AllocateAndAddFloatingIpToNode fn = requestsSendResponses(
-               ImmutableMap.<HttpRequest, HttpResponse> builder().put(keystoneAuthWithUsernameAndPasswordAndTenantName,
-                        responseWithKeystoneAccess).put(extensionsOfNovaRequest, extensionsOfNovaResponse).put(
-                        createFloatingIP, createFloatingIPResponse)
-                        .put(addFloatingIPRequest, addFloatingIPResponse).put(list,
-                                 listResponseForUnassigned).build()).getContext().utils().injector()
-               .getInstance(AllocateAndAddFloatingIpToNode.class);
+               ImmutableMap.<HttpRequest, HttpResponse> builder()
+                       .put(keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess)
+                       .put(listAZs, listAZsResponseForUnassigned)
+                       .put(extensionsOfNovaRequest, extensionsOfNovaResponse)
+                       .put(createFloatingIP, createFloatingIPResponse)
+                       .put(addFloatingIPRequest, addFloatingIPResponse)
+                       .put(list, listResponseForUnassigned).build())
+              .getContext().utils().injector()
+              .getInstance(AllocateAndAddFloatingIpToNode.class);
 
       AtomicReference<NodeMetadata> nodeRef = Atomics.newReference(node);
       AtomicReference<NovaTemplateOptions> optionsRef = Atomics.newReference(options);

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/loaders/LoadFloatingIpsForInstanceTest.java
----------------------------------------------------------------------
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 84d2fa7..6760c6d 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
@@ -25,6 +25,7 @@ import static org.testng.AssertJUnit.assertFalse;
 
 import org.jclouds.openstack.nova.v2_0.NovaApi;
 import org.jclouds.openstack.nova.v2_0.domain.FloatingIP;
+import org.jclouds.openstack.nova.v2_0.domain.FloatingIpForServer;
 import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndId;
 import org.jclouds.openstack.nova.v2_0.extensions.FloatingIPApi;
 import org.testng.annotations.Test;
@@ -50,8 +51,8 @@ public class LoadFloatingIpsForInstanceTest {
       replay(ipApi);
 
       LoadFloatingIpsForInstance parser = new LoadFloatingIpsForInstance(api);
-
-      assertEquals(ImmutableSet.copyOf(parser.load(RegionAndId.fromRegionAndId("RegionOne", "i-blah"))), ImmutableSet.of(testIp));
+      FloatingIpForServer floatingIpForServer = FloatingIpForServer.create(RegionAndId.fromRegionAndId("RegionOne", "i-blah"), "1", "1.1.1.1");
+      assertEquals(ImmutableSet.copyOf(parser.load(RegionAndId.fromRegionAndId("RegionOne", "i-blah"))), ImmutableSet.of(floatingIpForServer));
 
       verify(api);
       verify(ipApi);

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-nova/src/test/resources/availability_zone_list.json
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/test/resources/availability_zone_list.json b/apis/openstack-nova/src/test/resources/availability_zone_list.json
new file mode 100644
index 0000000..0058762
--- /dev/null
+++ b/apis/openstack-nova/src/test/resources/availability_zone_list.json
@@ -0,0 +1,18 @@
+{
+  "availabilityZoneInfo": [
+    {
+      "zoneState": {
+        "available": true
+      },
+      "hosts": null,
+      "zoneName": "uk-1a"
+    },
+    {
+      "zoneState": {
+        "available": true
+      },
+      "hosts": null,
+      "zoneName": "uk-1b"
+    }
+  ]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/s3/src/main/java/org/jclouds/s3/filters/Aws4SignerBase.java
----------------------------------------------------------------------
diff --git a/apis/s3/src/main/java/org/jclouds/s3/filters/Aws4SignerBase.java b/apis/s3/src/main/java/org/jclouds/s3/filters/Aws4SignerBase.java
index 615f0cf..5aa88da 100644
--- a/apis/s3/src/main/java/org/jclouds/s3/filters/Aws4SignerBase.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/filters/Aws4SignerBase.java
@@ -270,7 +270,6 @@ public abstract class Aws4SignerBase {
       }
    }
 
-
    /**
     * hash string (encoding UTF_8) with sha256
     *


[2/2] jclouds git commit: [JCLOUDS-1382] improve usage of Neutron capabilities from Nova

Posted by an...@apache.org.
[JCLOUDS-1382] improve usage of Neutron capabilities from Nova

- move Neutron FloatingIP API from extension to feature
- introduce FloatingIpForServer as abstract domain object to represents Nova and Neutron objects
- manage floating ip cache and its invalidation


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

Branch: refs/heads/master
Commit: 50ae6828f52cebe4dffab518c4d5053d93b2a494
Parents: 88c84af
Author: Andrea Turli <an...@gmail.com>
Authored: Wed Feb 14 16:03:57 2018 +0100
Committer: Andrea Turli <an...@gmail.com>
Committed: Thu Feb 15 18:08:53 2018 +0100

----------------------------------------------------------------------
 .../openstack/neutron/v2/NeutronApi.java        |  14 +-
 .../openstack/neutron/v2/domain/FloatingIP.java |  68 ++++-
 .../openstack/neutron/v2/domain/Network.java    |  31 +-
 .../openstack/neutron/v2/domain/Networks.java   |  23 ++
 .../neutron/v2/extensions/FloatingIPApi.java    | 139 ---------
 .../neutron/v2/features/FloatingIPApi.java      | 136 +++++++++
 .../functions/FloatingIPsToPagedIterable.java   |   4 +-
 .../neutron/v2/extensions/FWaaSApiMockTest.java |   7 +-
 .../v2/extensions/FloatingIPApiLiveTest.java    |  92 ------
 .../v2/extensions/FloatingIPApiMockTest.java    | 292 -------------------
 .../v2/features/FloatingIPApiLiveTest.java      |  77 +++++
 .../v2/features/FloatingIPApiMockTest.java      | 278 ++++++++++++++++++
 .../config/NovaComputeServiceContextModule.java |  12 +-
 .../AllocateAndAddFloatingIpToNode.java         | 184 ++++++++++--
 .../compute/functions/CleanupResources.java     |   1 +
 .../RemoveFloatingIpFromNodeAndDeallocate.java  |  46 ++-
 .../loaders/LoadFloatingIpsForInstance.java     |  14 +-
 .../nova/v2_0/domain/FloatingIpForServer.java   |  38 +++
 .../nova/v2_0/domain/PortInterface.java         |  57 ++++
 .../openstack/nova/v2_0/features/ServerApi.java |  15 +
 .../compute/NovaComputeServiceLiveTest.java     |  21 +-
 .../NovaWithNeutronComputeServiceLiveTest.java  | 130 +++++++++
 ...llocateAndAddFloatingIpToNodeExpectTest.java |  74 +++--
 .../loaders/LoadFloatingIpsForInstanceTest.java |   5 +-
 .../test/resources/availability_zone_list.json  |  18 ++
 .../org/jclouds/s3/filters/Aws4SignerBase.java  |   1 -
 26 files changed, 1141 insertions(+), 636 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/NeutronApi.java
----------------------------------------------------------------------
diff --git a/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/NeutronApi.java b/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/NeutronApi.java
index 97530f8..e7863d2 100644
--- a/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/NeutronApi.java
+++ b/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/NeutronApi.java
@@ -22,7 +22,7 @@ import java.util.Set;
 import javax.ws.rs.Path;
 
 import org.jclouds.location.Region;
-import org.jclouds.openstack.neutron.v2.extensions.FloatingIPApi;
+import org.jclouds.openstack.neutron.v2.features.FloatingIPApi;
 import org.jclouds.openstack.neutron.v2.extensions.RouterApi;
 import org.jclouds.openstack.neutron.v2.features.SecurityGroupApi;
 import org.jclouds.openstack.neutron.v2.extensions.lbaas.v1.LBaaSApi;
@@ -89,24 +89,20 @@ public interface NeutronApi extends Closeable {
    SecurityGroupApi getSecurityGroupApi(@EndpointParam(parser = VersionAwareRegionToEndpoint.class) String region);
 
    /**
-    * Provides access to Router features.
-    *
-    * <h3>NOTE</h3>
-    * This API is an extension that may or may not be present in your OpenStack cloud. Use the Optional return type
-    * to determine if it is present.
+    * Provides access to Floating IP features.
     */
    @Delegate
-   Optional<RouterApi> getRouterApi(@EndpointParam(parser = VersionAwareRegionToEndpoint.class) String region);
+   FloatingIPApi getFloatingIPApi(@EndpointParam(parser = VersionAwareRegionToEndpoint.class) String region);
 
    /**
-    * Provides access to Floating IP features.
+    * Provides access to Router features.
     *
     * <h3>NOTE</h3>
     * This API is an extension that may or may not be present in your OpenStack cloud. Use the Optional return type
     * to determine if it is present.
     */
    @Delegate
-   Optional<FloatingIPApi> getFloatingIPApi(@EndpointParam(parser = VersionAwareRegionToEndpoint.class) String region);
+   Optional<RouterApi> getRouterApi(@EndpointParam(parser = VersionAwareRegionToEndpoint.class) String region);
 
    /**
     * Provides access to LBaaS features.

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/FloatingIP.java
----------------------------------------------------------------------
diff --git a/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/FloatingIP.java b/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/FloatingIP.java
index be61ad3..dbb550c 100644
--- a/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/FloatingIP.java
+++ b/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/FloatingIP.java
@@ -34,6 +34,8 @@ public class FloatingIP {
    private String id;
    @Named("router_id")
    private String routerId;
+   @Named("project_id")
+   private String projectId;
    @Named("tenant_id")
    private String tenantId;
    // Only mandatory attribute when creating
@@ -45,21 +47,25 @@ public class FloatingIP {
    private String floatingIpAddress;
    @Named("port_id")
    private String portId;
+   @Named("availability_zone")
+   private String availabilityZone;
 
    /**
     * Deserialization constructor
     */
-   @ConstructorProperties({"id", "router_id", "tenant_id", "floating_network_id", "fixed_ip_address",
-         "floating_ip_address", "port_id"})
-   private FloatingIP(String id, String routerId, String tenantId, String floatingNetworkId, String fixedIpAddress,
-         String floatingIpAddress, String portId) {
+   @ConstructorProperties({"id", "router_id", "project_id", "tenant_id", "floating_network_id", "fixed_ip_address",
+         "floating_ip_address", "port_id", "availability_zone"})
+   private FloatingIP(String id, String routerId, String projectId, String tenantId, String floatingNetworkId, String fixedIpAddress,
+         String floatingIpAddress, String portId, String availabilityZone) {
       this.id = id;
       this.routerId = routerId;
+      this.projectId = projectId;
       this.tenantId = tenantId;
       this.floatingNetworkId = floatingNetworkId;
       this.fixedIpAddress = fixedIpAddress;
       this.floatingIpAddress = floatingIpAddress;
       this.portId = portId;
+      this.availabilityZone = availabilityZone;
    }
 
    private FloatingIP() {}
@@ -68,8 +74,8 @@ public class FloatingIP {
     * @param floatingIP The floating IP to copy from
     */
    private FloatingIP(FloatingIP floatingIP) {
-      this(floatingIP.id, floatingIP.routerId, floatingIP.tenantId, floatingIP.floatingNetworkId,
-            floatingIP.fixedIpAddress, floatingIP.floatingIpAddress, floatingIP.portId);
+      this(floatingIP.id, floatingIP.routerId, floatingIP.projectId, floatingIP.tenantId, floatingIP.floatingNetworkId,
+            floatingIP.fixedIpAddress, floatingIP.floatingIpAddress, floatingIP.portId, floatingIP.availabilityZone);
    }
 
    /**
@@ -89,6 +95,14 @@ public class FloatingIP {
    }
 
    /**
+    * @return the project id of the Floating IP
+    */
+   @Nullable
+   public String getProjectId() {
+      return projectId;
+   }
+
+   /**
     * @return the tenant id of the Floating IP
     */
    @Nullable
@@ -128,6 +142,14 @@ public class FloatingIP {
       return portId;
    }
 
+   /**
+    * @return the availability zone for this floating IP
+    */
+   @Nullable
+   public String getAvailabilityZone() {
+      return availabilityZone;
+   }
+
    @Override
    public boolean equals(Object o) {
       if (this == o)
@@ -139,17 +161,20 @@ public class FloatingIP {
 
       return Objects.equal(this.id, that.id) &&
             Objects.equal(this.routerId, that.routerId) &&
+            Objects.equal(this.projectId, that.projectId) &&
             Objects.equal(this.tenantId, that.tenantId) &&
             Objects.equal(this.floatingNetworkId, that.floatingNetworkId) &&
             Objects.equal(this.fixedIpAddress, that.fixedIpAddress) &&
             Objects.equal(this.floatingIpAddress, that.floatingIpAddress) &&
-            Objects.equal(this.portId, that.portId);
+            Objects.equal(this.portId, that.portId) &&
+            Objects.equal(this.availabilityZone, that.availabilityZone);
+
    }
 
    @Override
    public int hashCode() {
-      return Objects.hashCode(id, routerId, tenantId, floatingNetworkId, fixedIpAddress, floatingIpAddress,
-            portId);
+      return Objects.hashCode(id, routerId, projectId, tenantId, floatingNetworkId, fixedIpAddress, floatingIpAddress,
+            portId, availabilityZone);
    }
 
    @Override
@@ -157,11 +182,13 @@ public class FloatingIP {
       return MoreObjects.toStringHelper(this)
             .add("id", id)
             .add("routerId", routerId)
+            .add("projectId", projectId)
             .add("tenantId", tenantId)
             .add("floatingNetworkId", floatingNetworkId)
             .add("fixedIpAddress", fixedIpAddress)
             .add("floatingIpAddress", floatingIpAddress)
             .add("portId", portId)
+            .add("availabilityZone", availabilityZone)
             .toString();
    }
 
@@ -192,6 +219,17 @@ public class FloatingIP {
       protected abstract ParameterizedBuilderType self();
 
       /**
+       * Provide the project ID for this Floating IP.
+       *
+       * @return the Builder.
+       * @see FloatingIP#getProjectId() ()
+       */
+      public ParameterizedBuilderType projectId(String projectId) {
+         floatingIP.projectId = projectId;
+         return self();
+      }
+
+      /**
        * Provide the tenantId for this Floating IP. Admin-only.
        * When keystone is enabled, it is not mandatory to specify tenant_id for resources in create requests, as the
        * tenant identifier will be derived from the Authentication token. Please note that the default authorization
@@ -249,6 +287,18 @@ public class FloatingIP {
          floatingIP.portId = portId;
          return self();
       }
+
+      /**
+       * Provides the availability zone for this Floating IP.
+       *
+       * @return the Builder.
+       * @see FloatingIP#getAvailabilityZone()
+       */
+      public ParameterizedBuilderType availabilityZone(String availabilityZone) {
+         floatingIP.availabilityZone = availabilityZone;
+         return self();
+      }
+
    }
 
    public static class CreateBuilder extends Builder<CreateBuilder> {

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/Network.java
----------------------------------------------------------------------
diff --git a/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/Network.java b/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/Network.java
index c269593..d8fca09 100644
--- a/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/Network.java
+++ b/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/Network.java
@@ -47,6 +47,8 @@ public class Network {
    private Boolean shared;
    @Named("tenant_id")
    private String tenantId;
+   @Named("availability_zone")
+   private String availabilityZone;
 
    // providernet.py: Provider Networks Extension
    @Named("provider:network_type")
@@ -85,12 +87,12 @@ public class Network {
    @Named("flavor:network")
    private String networkFlavor;
 
-   @ConstructorProperties({"id", "status", "subnets", "name", "admin_state_up", "shared", "tenant_id",
+   @ConstructorProperties({"id", "status", "subnets", "name", "admin_state_up", "shared", "tenant_id", "availability_zone",
          "provider:network_type", "provider:physical_network", "provider:segmentation_id", "router:external",
          "port_security_enabled", "n1kv:profile_id", "n1kv:multicast_ip", "n1kv:segment_add", "n1kv:segment_del",
          "n1kv:member_segments", "segments", "flavor:network"})
    private Network(String id, NetworkStatus status, ImmutableSet<String> subnets, String name, Boolean adminStateUp,
-         Boolean shared, String tenantId, NetworkType networkType, String physicalNetworkName, Integer segmentationId,
+         Boolean shared, String tenantId, String availabilityZone, NetworkType networkType, String physicalNetworkName, Integer segmentationId,
          Boolean external, Boolean portSecurity, String profileId, String multicastIp, String segmentAdd,
          String segmentDel, String memberSegments, ImmutableSet<NetworkSegment> segments, String networkFlavor) {
       // No checkNotNulls. With Neutron, any of these properties can be left null when used in an update.
@@ -101,6 +103,7 @@ public class Network {
       this.adminStateUp = adminStateUp;
       this.shared = shared;
       this.tenantId = tenantId;
+      this.availabilityZone = availabilityZone;
       this.networkType = networkType;
       this.physicalNetworkName = physicalNetworkName;
       this.segmentationId = segmentationId;
@@ -132,6 +135,7 @@ public class Network {
       network.adminStateUp,
       network.shared,
       network.tenantId,
+      network.availabilityZone,
       network.networkType,
       network.physicalNetworkName,
       network.segmentationId,
@@ -206,6 +210,14 @@ public class Network {
    }
 
    /**
+    * @return the availability zone of the Network
+    */
+   @Nullable
+   public String getAvailabilityZone() {
+      return availabilityZone;
+   }
+
+   /**
     * @return the networkType of the Network
     */
    @Nullable
@@ -304,7 +316,7 @@ public class Network {
 
    @Override
    public int hashCode() {
-      return Objects.hashCode(id, status, subnets, name, adminStateUp, shared, tenantId, networkType,
+      return Objects.hashCode(id, status, subnets, name, adminStateUp, shared, tenantId, availabilityZone, networkType,
             physicalNetworkName, segmentationId, external, portSecurity, profileId, multicastIp, segmentAdd, segmentDel,
             memberSegments, segments, networkFlavor);
    }
@@ -323,6 +335,7 @@ public class Network {
             && Objects.equal(this.adminStateUp, that.adminStateUp)
             && Objects.equal(this.shared, that.shared)
             && Objects.equal(this.tenantId, that.tenantId)
+            && Objects.equal(this.availabilityZone, that.availabilityZone)
             && Objects.equal(this.networkType, that.networkType)
             && Objects.equal(this.physicalNetworkName, that.physicalNetworkName)
             && Objects.equal(this.segmentationId, that.segmentationId)
@@ -347,6 +360,7 @@ public class Network {
             .add("adminStateUp", adminStateUp)
             .add("shared", shared)
             .add("tenantId", tenantId)
+            .add("availabilityZone", availabilityZone)
             .add("networkType", networkType)
             .add("physicalNetworkName", physicalNetworkName)
             .add("segmentationId", segmentationId)
@@ -437,6 +451,17 @@ public class Network {
       }
 
       /**
+       * Provide the availabilityZone to the Network's Builder.
+       *
+       * @return the Builder.
+       * @see Network#getAvailabilityZone() ()
+       */
+      public ParameterizedBuilderType availabilityZone(String availabilityZone) {
+         network.availabilityZone = availabilityZone;
+         return self();
+      }
+
+      /**
        * Provide the networkType to the Network's Builder.
        *
        * @return the Builder.

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/Networks.java
----------------------------------------------------------------------
diff --git a/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/Networks.java b/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/Networks.java
index 9b3bf10..f47e13b 100644
--- a/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/Networks.java
+++ b/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/domain/Networks.java
@@ -17,7 +17,9 @@
 package org.jclouds.openstack.neutron.v2.domain;
 
 import java.beans.ConstructorProperties;
+import java.util.Set;
 
+import com.google.common.base.Predicate;
 import org.jclouds.openstack.v2_0.domain.Link;
 import org.jclouds.openstack.v2_0.domain.PaginatedCollection;
 
@@ -33,4 +35,25 @@ public class Networks extends PaginatedCollection<Network> {
    protected Networks(Iterable<Network> networks, Iterable<Link> networksLinks) {
       super(networks, networksLinks);
    }
+
+   public static class Predicates {
+
+      public static Predicate<Network> externalNetworks(final String availabilityZone) {
+         return new Predicate<Network>() {
+            @Override
+            public boolean apply(Network network) {
+               return availabilityZone.equals(network.getAvailabilityZone()) && network.getExternal();
+            }
+         };
+      }
+
+      public static Predicate<Network> namedNetworks(final Set<String> names) {
+         return new Predicate<Network>() {
+            @Override
+            public boolean apply(Network network) {
+               return names.contains(network.getName());
+            }
+         };
+      }
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/extensions/FloatingIPApi.java
----------------------------------------------------------------------
diff --git a/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/extensions/FloatingIPApi.java b/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/extensions/FloatingIPApi.java
deleted file mode 100644
index a00bc04..0000000
--- a/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/extensions/FloatingIPApi.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jclouds.openstack.neutron.v2.extensions;
-
-import javax.inject.Named;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.core.MediaType;
-
-import org.jclouds.Fallbacks.EmptyPagedIterableOnNotFoundOr404;
-import org.jclouds.Fallbacks.FalseOnNotFoundOr404;
-import org.jclouds.Fallbacks.NullOnNotFoundOr404;
-import org.jclouds.collect.PagedIterable;
-import org.jclouds.javax.annotation.Nullable;
-import org.jclouds.openstack.keystone.auth.filters.AuthenticateRequest;
-import org.jclouds.openstack.neutron.v2.domain.FloatingIP;
-import org.jclouds.openstack.neutron.v2.domain.FloatingIPs;
-import org.jclouds.openstack.neutron.v2.fallbacks.EmptyFloatingIPsFallback;
-import org.jclouds.openstack.neutron.v2.functions.FloatingIPsToPagedIterable;
-import org.jclouds.openstack.neutron.v2.functions.ParseFloatingIPs;
-import org.jclouds.openstack.v2_0.ServiceType;
-import org.jclouds.openstack.v2_0.options.PaginationOptions;
-import org.jclouds.openstack.v2_0.services.Extension;
-import org.jclouds.rest.annotations.Fallback;
-import org.jclouds.rest.annotations.RequestFilters;
-import org.jclouds.rest.annotations.ResponseParser;
-import org.jclouds.rest.annotations.SelectJson;
-import org.jclouds.rest.annotations.Transform;
-import org.jclouds.rest.annotations.WrapWith;
-
-import com.google.common.annotations.Beta;
-
-/**
- * Provides access to Floating IP operations for the OpenStack Networking (Neutron) v2 API.
- * <p/>
- * A floating IP is an IP address on an external network, which is associated with a specific port, and optionally a
- * specific IP address, on a private OpenStack Networking network. Therefore a floating IP allows access to an
- * instance on a private network from an external network. Floating IPs can only be defined on networks for which the
- * attribute floatingip:external (by the external network extension) has been set to True.
- */
-@Beta
-@Path("/floatingips")
-@RequestFilters(AuthenticateRequest.class)
-@Consumes(MediaType.APPLICATION_JSON)
-@Extension(of = ServiceType.NETWORK, namespace = ExtensionNamespaces.L3_ROUTER, name = "Neutron L3 Router", alias = "router")
-public interface FloatingIPApi {
-
-   /**
-    * Returns a list of floating IPs to which the tenant has access. Default policy settings return only
-    * those floating IPs that are owned by the tenant who submits the request, unless the request is submitted by an
-    * user with administrative rights.
-    *
-    * @return the list of all floatingIP references configured for the tenant.
-    */
-   @Named("floatingip:list")
-   @GET
-   @Transform(FloatingIPsToPagedIterable.class)
-   @ResponseParser(ParseFloatingIPs.class)
-   @Fallback(EmptyPagedIterableOnNotFoundOr404.class)
-   PagedIterable<FloatingIP> list();
-
-   /**
-    * @return the list of all floatingIP references configured for the tenant.
-    */
-   @Named("floatingip:list")
-   @GET
-   @ResponseParser(ParseFloatingIPs.class)
-   @Fallback(EmptyFloatingIPsFallback.class)
-   FloatingIPs list(PaginationOptions options);
-
-   /**
-    * Returns the details for a specific floating IP.
-    *
-    * @param id the id of the floatingIP to return
-    * @return FloatingIPs collection or empty if not found
-    */
-   @Named("floatingip:get")
-   @GET
-   @Path("/{id}")
-   @SelectJson("floatingip")
-   @Fallback(NullOnNotFoundOr404.class)
-   @Nullable
-   FloatingIP get(@PathParam("id") String id);
-
-   /**
-    * Creates a floating IP.
-    *
-    * @param createFloatingIP Options for creating a Floating IP
-    * @return the newly created Floating IP
-    */
-   @Named("floatingip:create")
-   @POST
-   @SelectJson("floatingip")
-   FloatingIP create(@WrapWith("floatingip") FloatingIP.CreateFloatingIP createFloatingIP);
-
-   /**
-    * Update a Floating IP
-    *
-    * @param id the id of the Floating IP to update
-    * @param updateFloatingIP Contains only the attributes to update
-    * @return The modified Floating IP
-    */
-   @Named("floatingip:update")
-   @PUT
-   @Path("/{id}")
-   @SelectJson("floatingip")
-   FloatingIP update(@PathParam("id") String id, @WrapWith("floatingip") FloatingIP.UpdateFloatingIP updateFloatingIP);
-
-   /**
-    * Deletes the specified floatingIP
-    *
-    * @param id the id of the floatingIP to delete
-    * @return true if delete successful, false if not
-    */
-   @Named("floatingip:delete")
-   @DELETE
-   @Path("/{id}")
-   @Fallback(FalseOnNotFoundOr404.class)
-   boolean delete(@PathParam("id") String id);
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/features/FloatingIPApi.java
----------------------------------------------------------------------
diff --git a/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/features/FloatingIPApi.java b/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/features/FloatingIPApi.java
new file mode 100644
index 0000000..43d7b3c
--- /dev/null
+++ b/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/features/FloatingIPApi.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.neutron.v2.features;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.Fallbacks.EmptyPagedIterableOnNotFoundOr404;
+import org.jclouds.Fallbacks.FalseOnNotFoundOr404;
+import org.jclouds.Fallbacks.NullOnNotFoundOr404;
+import org.jclouds.collect.PagedIterable;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.openstack.keystone.auth.filters.AuthenticateRequest;
+import org.jclouds.openstack.neutron.v2.domain.FloatingIP;
+import org.jclouds.openstack.neutron.v2.domain.FloatingIPs;
+import org.jclouds.openstack.neutron.v2.fallbacks.EmptyFloatingIPsFallback;
+import org.jclouds.openstack.neutron.v2.functions.FloatingIPsToPagedIterable;
+import org.jclouds.openstack.neutron.v2.functions.ParseFloatingIPs;
+import org.jclouds.openstack.v2_0.options.PaginationOptions;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.ResponseParser;
+import org.jclouds.rest.annotations.SelectJson;
+import org.jclouds.rest.annotations.Transform;
+import org.jclouds.rest.annotations.WrapWith;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Provides access to Floating IP operations for the OpenStack Networking (Neutron) v2 API.
+ * <p/>
+ * A floating IP is an IP address on an external network, which is associated with a specific port, and optionally a
+ * specific IP address, on a private OpenStack Networking network. Therefore a floating IP allows access to an
+ * instance on a private network from an external network. Floating IPs can only be defined on networks for which the
+ * attribute floatingip:external (by the external network extension) has been set to True.
+ */
+@Beta
+@Path("/floatingips")
+@RequestFilters(AuthenticateRequest.class)
+@Consumes(MediaType.APPLICATION_JSON)
+public interface FloatingIPApi {
+
+   /**
+    * Returns a list of floating IPs to which the tenant has access. Default policy settings return only
+    * those floating IPs that are owned by the tenant who submits the request, unless the request is submitted by an
+    * user with administrative rights.
+    *
+    * @return the list of all floatingIP references configured for the tenant.
+    */
+   @Named("floatingip:list")
+   @GET
+   @Transform(FloatingIPsToPagedIterable.class)
+   @ResponseParser(ParseFloatingIPs.class)
+   @Fallback(EmptyPagedIterableOnNotFoundOr404.class)
+   PagedIterable<FloatingIP> list();
+
+   /**
+    * @return the list of all floatingIP references configured for the tenant.
+    */
+   @Named("floatingip:list")
+   @GET
+   @ResponseParser(ParseFloatingIPs.class)
+   @Fallback(EmptyFloatingIPsFallback.class)
+   FloatingIPs list(PaginationOptions options);
+
+   /**
+    * Returns the details for a specific floating IP.
+    *
+    * @param id the id of the floatingIP to return
+    * @return FloatingIPs collection or empty if not found
+    */
+   @Named("floatingip:get")
+   @GET
+   @Path("/{id}")
+   @SelectJson("floatingip")
+   @Fallback(NullOnNotFoundOr404.class)
+   @Nullable
+   FloatingIP get(@PathParam("id") String id);
+
+   /**
+    * Creates a floating IP.
+    *
+    * @param createFloatingIP Options for creating a Floating IP
+    * @return the newly created Floating IP
+    */
+   @Named("floatingip:create")
+   @POST
+   @SelectJson("floatingip")
+   FloatingIP create(@WrapWith("floatingip") FloatingIP.CreateFloatingIP createFloatingIP);
+
+   /**
+    * Update a Floating IP
+    *
+    * @param id the id of the Floating IP to update
+    * @param updateFloatingIP Contains only the attributes to update
+    * @return The modified Floating IP
+    */
+   @Named("floatingip:update")
+   @PUT
+   @Path("/{id}")
+   @SelectJson("floatingip")
+   FloatingIP update(@PathParam("id") String id, @WrapWith("floatingip") FloatingIP.UpdateFloatingIP updateFloatingIP);
+
+   /**
+    * Deletes the specified floatingIP
+    *
+    * @param id the id of the floatingIP to delete
+    * @return true if delete successful, false if not
+    */
+   @Named("floatingip:delete")
+   @DELETE
+   @Path("/{id}")
+   @Fallback(FalseOnNotFoundOr404.class)
+   boolean delete(@PathParam("id") String id);
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/FloatingIPsToPagedIterable.java
----------------------------------------------------------------------
diff --git a/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/FloatingIPsToPagedIterable.java b/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/FloatingIPsToPagedIterable.java
index 4d54d1f..058ea3f 100644
--- a/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/FloatingIPsToPagedIterable.java
+++ b/apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/functions/FloatingIPsToPagedIterable.java
@@ -22,7 +22,7 @@ import org.jclouds.collect.IterableWithMarker;
 import org.jclouds.collect.internal.Arg0ToPagedIterable;
 import org.jclouds.openstack.neutron.v2.NeutronApi;
 import org.jclouds.openstack.neutron.v2.domain.FloatingIP;
-import org.jclouds.openstack.neutron.v2.extensions.FloatingIPApi;
+import org.jclouds.openstack.neutron.v2.features.FloatingIPApi;
 import org.jclouds.openstack.v2_0.options.PaginationOptions;
 
 import javax.inject.Inject;
@@ -44,7 +44,7 @@ public class FloatingIPsToPagedIterable extends Arg0ToPagedIterable.FromCaller<F
    @Override
    protected Function<Object, IterableWithMarker<FloatingIP>> markerToNextForArg0(Optional<Object> arg0) {
       String region = arg0.isPresent() ? arg0.get().toString() : null;
-      final FloatingIPApi floatingIPApi = api.getFloatingIPApi(region).get();
+      final FloatingIPApi floatingIPApi = api.getFloatingIPApi(region);
       return new Function<Object, IterableWithMarker<FloatingIP>>() {
 
          @SuppressWarnings("unchecked")

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/extensions/FWaaSApiMockTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/extensions/FWaaSApiMockTest.java b/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/extensions/FWaaSApiMockTest.java
index ae84c36..595b22f 100644
--- a/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/extensions/FWaaSApiMockTest.java
+++ b/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/extensions/FWaaSApiMockTest.java
@@ -36,6 +36,7 @@ import org.jclouds.openstack.neutron.v2.domain.FloatingIP;
 import org.jclouds.openstack.neutron.v2.domain.UpdateFirewall;
 import org.jclouds.openstack.neutron.v2.domain.UpdateFirewallPolicy;
 import org.jclouds.openstack.neutron.v2.domain.UpdateFirewallRule;
+import org.jclouds.openstack.neutron.v2.features.FloatingIPApi;
 import org.jclouds.openstack.neutron.v2.internal.BaseNeutronApiMockTest;
 import org.jclouds.openstack.v2_0.domain.PaginatedCollection;
 import org.jclouds.openstack.v2_0.options.PaginationOptions;
@@ -130,13 +131,12 @@ public class FWaaSApiMockTest extends BaseNeutronApiMockTest {
    public void testListPagedFirewall() throws IOException, InterruptedException, URISyntaxException {
       MockWebServer server = mockOpenStackServer();
       server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
-      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/extension_list.json"))));
       server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(200).setBody(stringFromResource("/floatingip_list_response_paged1.json"))));
       server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(200).setBody(stringFromResource("/floatingip_list_response_paged2.json"))));
 
       try {
          NeutronApi neutronApi = api(server.getUrl("/").toString(), "openstack-neutron", overrides);
-         FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne").get();
+         FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne");
 
          // Note: Lazy! Have to actually look at the collection.
          List<FloatingIP> floatingIPs = api.list().concat().toList();
@@ -144,9 +144,8 @@ public class FWaaSApiMockTest extends BaseNeutronApiMockTest {
          /*
           * Check request
           */
-         assertEquals(server.getRequestCount(), 4);
+         assertEquals(server.getRequestCount(), 3);
          assertAuthentication(server);
-         assertExtensions(server, uriApiVersion + "");
          assertRequest(server.takeRequest(), "GET", uriApiVersion + "/floatingips");
          assertRequest(server.takeRequest(), "GET", uriApiVersion + "/floatingips?marker=71c1e68c-171a-4aa2-aca5-50ea153a3718");
 

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/extensions/FloatingIPApiLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/extensions/FloatingIPApiLiveTest.java b/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/extensions/FloatingIPApiLiveTest.java
deleted file mode 100644
index a3d5f3d..0000000
--- a/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/extensions/FloatingIPApiLiveTest.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jclouds.openstack.neutron.v2.extensions;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
-
-import java.util.Set;
-
-import org.jclouds.openstack.neutron.v2.domain.FloatingIP;
-import org.jclouds.openstack.neutron.v2.domain.IP;
-import org.jclouds.openstack.neutron.v2.domain.Network;
-import org.jclouds.openstack.neutron.v2.domain.NetworkType;
-import org.jclouds.openstack.neutron.v2.domain.Subnet;
-import org.jclouds.openstack.neutron.v2.features.NetworkApi;
-import org.jclouds.openstack.neutron.v2.features.SubnetApi;
-import org.jclouds.openstack.neutron.v2.internal.BaseNeutronApiLiveTest;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.ImmutableSet;
-
-/**
- * Tests parsing and Guice wiring of RouterApi
- */
-@Test(groups = "live", testName = "FloatingIPApiLiveTest")
-public class FloatingIPApiLiveTest extends BaseNeutronApiLiveTest {
-
-   public void testCreateUpdateAndDeleteFloatingIP() {
-      for (String region : api.getConfiguredRegions()) {
-
-         SubnetApi subnetApi = api.getSubnetApi(region);
-         FloatingIPApi floatingIPApi = api.getFloatingIPApi(region).get();
-         NetworkApi networkApi = api.getNetworkApi(region);
-
-         FloatingIP floatingIPGet = null;
-         String ipv4SubnetId = null;
-         Network network = null;
-
-         try {
-            network = networkApi.create(
-                  Network.createBuilder("jclouds-network-test").external(true).networkType(NetworkType.LOCAL).build());
-            assertNotNull(network);
-
-            ipv4SubnetId = subnetApi.create(Subnet.createBuilder(network.getId(), "198.51.100.0/24").ipVersion(4)
-                  .name("JClouds-Live-IPv4-Subnet").build()).getId();
-
-            floatingIPApi.create(FloatingIP.createBuilder(network.getId()).build());
-
-            /* List and Get test */
-            Set<FloatingIP> floatingIPs = floatingIPApi.list().concat().toSet();
-            FloatingIP floatingIPList = floatingIPs.iterator().next();
-            floatingIPGet = floatingIPApi.get(floatingIPList.getId());
-
-            assertNotNull(floatingIPGet);
-            assertEquals(floatingIPGet, floatingIPList);
-         }
-         finally {
-            try {
-               assertTrue(floatingIPApi.delete(floatingIPGet.getId()));
-            }
-            finally {
-               try {
-                  assertTrue(subnetApi.delete(ipv4SubnetId));
-               }
-               finally {
-                  assertTrue(networkApi.delete(network.getId()));
-               }
-            }
-         }
-      }
-   }
-
-   public Set<IP> getFixedAddresses(String subnetId) {
-      return ImmutableSet.of(IP.builder().subnetId(subnetId).build());
-   }
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/extensions/FloatingIPApiMockTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/extensions/FloatingIPApiMockTest.java b/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/extensions/FloatingIPApiMockTest.java
deleted file mode 100644
index ea368ee..0000000
--- a/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/extensions/FloatingIPApiMockTest.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jclouds.openstack.neutron.v2.extensions;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
-
-import java.io.IOException;
-import java.net.URISyntaxException;
-import java.util.List;
-
-import org.jclouds.openstack.neutron.v2.NeutronApi;
-import org.jclouds.openstack.neutron.v2.domain.FloatingIP;
-import org.jclouds.openstack.neutron.v2.domain.FloatingIPs;
-import org.jclouds.openstack.neutron.v2.internal.BaseNeutronApiMockTest;
-import org.jclouds.openstack.v2_0.options.PaginationOptions;
-import org.testng.annotations.Test;
-
-import com.squareup.okhttp.mockwebserver.MockResponse;
-import com.squareup.okhttp.mockwebserver.MockWebServer;
-
-/**
- * Tests Floating Api Guice wiring and parsing
- *
- */
-@Test
-public class FloatingIPApiMockTest extends BaseNeutronApiMockTest {
-
-   public void testCreateFloatingIP() throws IOException, InterruptedException, URISyntaxException {
-      MockWebServer server = mockOpenStackServer();
-      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
-      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/extension_list.json"))));
-      server.enqueue(addCommonHeaders(
-            new MockResponse().setResponseCode(201).setBody(stringFromResource("/floatingip_create_response.json"))));
-
-      try {
-         NeutronApi neutronApi = api(server.getUrl("/").toString(), "openstack-neutron", overrides);
-         FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne").get();
-
-         FloatingIP.CreateFloatingIP createFip = FloatingIP.createBuilder("376da547-b977-4cfe-9cba-275c80debf57")
-               .portId("ce705c24-c1ef-408a-bda3-7bbd946164ab")
-               .build();
-
-         FloatingIP floatingIP = api.create(createFip);
-
-         /*
-          * Check request
-          */
-         assertEquals(server.getRequestCount(), 3);
-         assertAuthentication(server);
-         assertExtensions(server, uriApiVersion + "");
-         assertRequest(server.takeRequest(), "POST", uriApiVersion + "/floatingips", "/floatingip_create_request.json");
-
-         /*
-          * Check response
-          */
-         assertNotNull(floatingIP);
-         assertEquals(floatingIP.getRouterId(), "d23abc8d-2991-4a55-ba98-2aaea84cc72f");
-         assertEquals(floatingIP.getTenantId(), "4969c491a3c74ee4af974e6d800c62de");
-         assertEquals(floatingIP.getFloatingNetworkId(), "376da547-b977-4cfe-9cba-275c80debf57");
-         assertEquals(floatingIP.getFixedIpAddress(), "10.0.0.3");
-         assertEquals(floatingIP.getFloatingIpAddress(), "172.24.4.228");
-         assertEquals(floatingIP.getPortId(), "ce705c24-c1ef-408a-bda3-7bbd946164ab");
-         assertEquals(floatingIP.getId(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7");
-
-      } finally {
-         server.shutdown();
-      }
-   }
-
-   public void testListSpecificPageFloatingIP() throws IOException, InterruptedException, URISyntaxException {
-      MockWebServer server = mockOpenStackServer();
-      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
-      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/extension_list.json"))));
-      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(200).setBody(stringFromResource("/floatingip_list_response_paged1.json"))));
-
-      try {
-         NeutronApi neutronApi = api(server.getUrl("/").toString(), "openstack-neutron", overrides);
-         FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne").get();
-
-         FloatingIPs floatingIPs = api.list(PaginationOptions.Builder.limit(2).marker("abcdefg"));
-
-         /*
-          * Check request
-          */
-         assertEquals(server.getRequestCount(), 3);
-         assertAuthentication(server);
-         assertExtensions(server, uriApiVersion + "");
-         assertRequest(server.takeRequest(), "GET", uriApiVersion + "/floatingips?limit=2&marker=abcdefg");
-
-         /*
-          * Check response
-          */
-         assertNotNull(floatingIPs);
-         assertEquals(floatingIPs.size(), 2);
-         assertEquals(floatingIPs.first().get().getId(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7");
-         assertEquals(floatingIPs.get(1).getId(), "61cea855-49cb-4846-997d-801b70c71bdd");
-      } finally {
-         server.shutdown();
-      }
-   }
-
-   public void testListPagedFloatingIP() throws IOException, InterruptedException, URISyntaxException {
-      MockWebServer server = mockOpenStackServer();
-      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
-      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/extension_list.json"))));
-      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(200).setBody(stringFromResource("/floatingip_list_response_paged1.json"))));
-      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(200).setBody(stringFromResource("/floatingip_list_response_paged2.json"))));
-
-      try {
-         NeutronApi neutronApi = api(server.getUrl("/").toString(), "openstack-neutron", overrides);
-         FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne").get();
-
-         // Note: Lazy! Have to actually look at the collection.
-         List<FloatingIP> floatingIPs = api.list().concat().toList();
-
-         /*
-          * Check request
-          */
-         assertEquals(server.getRequestCount(), 4);
-         assertAuthentication(server);
-         assertExtensions(server, uriApiVersion + "");
-         assertRequest(server.takeRequest(), "GET", uriApiVersion + "/floatingips");
-         assertRequest(server.takeRequest(), "GET", uriApiVersion + "/floatingips?marker=71c1e68c-171a-4aa2-aca5-50ea153a3718");
-
-         /*
-          * Check response
-          */
-         assertNotNull(floatingIPs);
-         assertEquals(floatingIPs.size(), 4);
-         assertEquals(floatingIPs.get(0).getId(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7");
-         assertEquals(floatingIPs.get(3).getId(), "61cea855-49cb-4846-997d-801b70c71bdd2");
-      } finally {
-         server.shutdown();
-      }
-   }
-
-   public void testGetFloatingIP() throws IOException, InterruptedException, URISyntaxException {
-      MockWebServer server = mockOpenStackServer();
-      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
-      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/extension_list.json"))));
-      server.enqueue(addCommonHeaders(
-            new MockResponse().setResponseCode(201).setBody(stringFromResource("/floatingip_get_response.json"))));
-
-      try {
-         NeutronApi neutronApi = api(server.getUrl("/").toString(), "openstack-neutron", overrides);
-         FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne").get();
-
-         FloatingIP floatingIP = api.get("12345");
-
-         /*
-          * Check request
-          */
-         assertEquals(server.getRequestCount(), 3);
-         assertAuthentication(server);
-         assertExtensions(server, uriApiVersion + "");
-         assertRequest(server.takeRequest(), "GET", uriApiVersion + "/floatingips/12345");
-
-         /*
-          * Check response
-          */
-         assertNotNull(floatingIP);
-         assertEquals(floatingIP.getId(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7");
-         assertEquals(floatingIP.getPortId(), "ce705c24-c1ef-408a-bda3-7bbd946164ab");
-         assertEquals(floatingIP.getFloatingIpAddress(), "172.24.4.228");
-         assertEquals(floatingIP.getFixedIpAddress(), "10.0.0.3");
-         assertEquals(floatingIP.getFloatingNetworkId(), "376da547-b977-4cfe-9cba-275c80debf57");
-         assertEquals(floatingIP.getRouterId(), "d23abc8d-2991-4a55-ba98-2aaea84cc72f");
-         assertEquals(floatingIP.getTenantId(), "4969c491a3c74ee4af974e6d800c62de");
-
-      } finally {
-         server.shutdown();
-      }
-   }
-
-   public void testUpdateFloatingIP() throws IOException, InterruptedException, URISyntaxException {
-      MockWebServer server = mockOpenStackServer();
-      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
-      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/extension_list.json"))));
-      server.enqueue(addCommonHeaders(
-            new MockResponse().setResponseCode(201).setBody(stringFromResource("/floatingip_update_response.json"))));
-
-      try {
-         NeutronApi neutronApi = api(server.getUrl("/").toString(), "openstack-neutron", overrides);
-         FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne").get();
-
-         FloatingIP.UpdateFloatingIP updateFloatingIP = FloatingIP.updateBuilder()
-               .portId("fc861431-0e6c-4842-a0ed-e2363f9bc3a8")
-               .build();
-
-         FloatingIP floatingIP = api.update("12345", updateFloatingIP);
-
-         /*
-          * Check request
-          */
-         assertEquals(server.getRequestCount(), 3);
-         assertAuthentication(server);
-         assertExtensions(server, uriApiVersion + "");
-         assertRequest(server.takeRequest(), "PUT", uriApiVersion + "/floatingips/12345", "/floatingip_update_request.json");
-
-         /*
-          * Check response
-          */
-         assertNotNull(floatingIP);
-         assertEquals(floatingIP.getPortId(), "fc861431-0e6c-4842-a0ed-e2363f9bc3a8");
-
-      } finally {
-         server.shutdown();
-      }
-   }
-
-   public void testUpdateFloatingIPDissociate() throws IOException, InterruptedException, URISyntaxException {
-      MockWebServer server = mockOpenStackServer();
-      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
-      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/extension_list.json"))));
-      server.enqueue(addCommonHeaders(
-            new MockResponse().setResponseCode(201).setBody(stringFromResource("/floatingip_update_dissociate_response.json"))));
-
-      try {
-         NeutronApi neutronApi = api(server.getUrl("/").toString(), "openstack-neutron", overrides);
-         FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne").get();
-
-         FloatingIP.UpdateFloatingIP updateFloatingIP = FloatingIP.updateBuilder().build();
-
-         FloatingIP floatingIP = api.update("12345", updateFloatingIP);
-
-         /*
-          * Check request
-          */
-         assertEquals(server.getRequestCount(), 3);
-         assertAuthentication(server);
-         assertExtensions(server, uriApiVersion + "");
-         assertRequest(server.takeRequest(), "PUT", uriApiVersion + "/floatingips/12345", "/floatingip_update_dissociate_request.json");
-
-         /*
-          * Check response
-          */
-         assertNotNull(floatingIP);
-         assertNull(floatingIP.getPortId());
-
-      } finally {
-         server.shutdown();
-      }
-   }
-
-   public void testDeleteFloatingIP() throws IOException, InterruptedException, URISyntaxException {
-      MockWebServer server = mockOpenStackServer();
-      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
-      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/extension_list.json"))));
-      server.enqueue(addCommonHeaders(
-            new MockResponse().setResponseCode(201)));
-
-      try {
-         NeutronApi neutronApi = api(server.getUrl("/").toString(), "openstack-neutron", overrides);
-         FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne").get();
-
-         boolean result = api.delete("12345");
-
-         /*
-          * Check request
-          */
-         assertEquals(server.getRequestCount(), 3);
-         assertAuthentication(server);
-         assertExtensions(server, uriApiVersion + "");
-         assertRequest(server.takeRequest(), "DELETE", uriApiVersion + "/floatingips/12345");
-
-         /*
-          * Check response
-          */
-         assertTrue(result);
-      } finally {
-         server.shutdown();
-      }
-   }
-
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/features/FloatingIPApiLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/features/FloatingIPApiLiveTest.java b/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/features/FloatingIPApiLiveTest.java
new file mode 100644
index 0000000..a960884
--- /dev/null
+++ b/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/features/FloatingIPApiLiveTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jclouds.openstack.neutron.v2.features;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Set;
+
+import com.google.common.base.Predicate;
+import org.jclouds.openstack.neutron.v2.domain.FloatingIP;
+import org.jclouds.openstack.neutron.v2.domain.IP;
+import org.jclouds.openstack.neutron.v2.domain.Network;
+import org.jclouds.openstack.neutron.v2.internal.BaseNeutronApiLiveTest;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Tests parsing and Guice wiring of FloatingIPApi
+ */
+@Test(groups = "live", testName = "FloatingIPApiLiveTest")
+public class FloatingIPApiLiveTest extends BaseNeutronApiLiveTest {
+
+   public void testCreateUpdateAndDeleteFloatingIP() {
+      for (String region : api.getConfiguredRegions()) {
+
+         FloatingIPApi floatingIPApi = api.getFloatingIPApi(region);
+         NetworkApi networkApi = api.getNetworkApi(region);
+
+         FloatingIP floatingIPGet = null;
+         Network network;
+
+         try {
+            network = networkApi.list().concat().firstMatch(new Predicate<Network>() {
+               @Override
+               public boolean apply(Network input) {
+                  return input.getExternal();
+               }
+            }).orNull();
+
+            if (network == null) Assert.fail("Cannot find a suitable external network. Please add it manually or contact your administrator");
+
+            FloatingIP floatingIP = floatingIPApi.create(FloatingIP.createBuilder(network.getId()).availabilityZone(network.getAvailabilityZone()).build());
+            /* List and Get test */
+            Set<FloatingIP> floatingIPs = floatingIPApi.list().concat().toSet();
+            floatingIPGet = floatingIPApi.get(floatingIP.getId());
+
+            assertNotNull(floatingIPGet);
+            assertTrue(floatingIPs.contains(floatingIP));
+         }
+         finally {
+            assertTrue(floatingIPApi.delete(floatingIPGet.getId()));
+         }
+      }
+   }
+
+   public Set<IP> getFixedAddresses(String subnetId) {
+      return ImmutableSet.of(IP.builder().subnetId(subnetId).build());
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/features/FloatingIPApiMockTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/features/FloatingIPApiMockTest.java b/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/features/FloatingIPApiMockTest.java
new file mode 100644
index 0000000..b34d8ab
--- /dev/null
+++ b/apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/features/FloatingIPApiMockTest.java
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.neutron.v2.features;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.jclouds.openstack.neutron.v2.NeutronApi;
+import org.jclouds.openstack.neutron.v2.domain.FloatingIP;
+import org.jclouds.openstack.neutron.v2.domain.FloatingIPs;
+import org.jclouds.openstack.neutron.v2.internal.BaseNeutronApiMockTest;
+import org.jclouds.openstack.v2_0.options.PaginationOptions;
+import org.testng.annotations.Test;
+
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+
+/**
+ * Tests Floating Api Guice wiring and parsing
+ *
+ */
+@Test
+public class FloatingIPApiMockTest extends BaseNeutronApiMockTest {
+
+   public void testCreateFloatingIP() throws IOException, InterruptedException, URISyntaxException {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(
+            new MockResponse().setResponseCode(201).setBody(stringFromResource("/floatingip_create_response.json"))));
+
+      try {
+         NeutronApi neutronApi = api(server.getUrl("/").toString(), "openstack-neutron", overrides);
+         FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne");
+
+         FloatingIP.CreateFloatingIP createFip = FloatingIP.createBuilder("376da547-b977-4cfe-9cba-275c80debf57")
+               .portId("ce705c24-c1ef-408a-bda3-7bbd946164ab")
+               .build();
+
+         FloatingIP floatingIP = api.create(createFip);
+
+         /*
+          * Check request
+          */
+         assertEquals(server.getRequestCount(), 2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "POST", uriApiVersion + "/floatingips", "/floatingip_create_request.json");
+
+         /*
+          * Check response
+          */
+         assertNotNull(floatingIP);
+         assertEquals(floatingIP.getRouterId(), "d23abc8d-2991-4a55-ba98-2aaea84cc72f");
+         assertEquals(floatingIP.getTenantId(), "4969c491a3c74ee4af974e6d800c62de");
+         assertEquals(floatingIP.getFloatingNetworkId(), "376da547-b977-4cfe-9cba-275c80debf57");
+         assertEquals(floatingIP.getFixedIpAddress(), "10.0.0.3");
+         assertEquals(floatingIP.getFloatingIpAddress(), "172.24.4.228");
+         assertEquals(floatingIP.getPortId(), "ce705c24-c1ef-408a-bda3-7bbd946164ab");
+         assertEquals(floatingIP.getId(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7");
+
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testListSpecificPageFloatingIP() throws IOException, InterruptedException, URISyntaxException {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(200).setBody(stringFromResource("/floatingip_list_response_paged1.json"))));
+
+      try {
+         NeutronApi neutronApi = api(server.getUrl("/").toString(), "openstack-neutron", overrides);
+         FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne");
+
+         FloatingIPs floatingIPs = api.list(PaginationOptions.Builder.limit(2).marker("abcdefg"));
+
+         /*
+          * Check request
+          */
+         assertEquals(server.getRequestCount(), 2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "GET", uriApiVersion + "/floatingips?limit=2&marker=abcdefg");
+
+         /*
+          * Check response
+          */
+         assertNotNull(floatingIPs);
+         assertEquals(floatingIPs.size(), 2);
+         assertEquals(floatingIPs.first().get().getId(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7");
+         assertEquals(floatingIPs.get(1).getId(), "61cea855-49cb-4846-997d-801b70c71bdd");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testListPagedFloatingIP() throws IOException, InterruptedException, URISyntaxException {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(200).setBody(stringFromResource("/floatingip_list_response_paged1.json"))));
+      server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(200).setBody(stringFromResource("/floatingip_list_response_paged2.json"))));
+
+      try {
+         NeutronApi neutronApi = api(server.getUrl("/").toString(), "openstack-neutron", overrides);
+         FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne");
+
+         // Note: Lazy! Have to actually look at the collection.
+         List<FloatingIP> floatingIPs = api.list().concat().toList();
+
+         /*
+          * Check request
+          */
+         assertEquals(server.getRequestCount(), 3);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "GET", uriApiVersion + "/floatingips");
+         assertRequest(server.takeRequest(), "GET", uriApiVersion + "/floatingips?marker=71c1e68c-171a-4aa2-aca5-50ea153a3718");
+
+         /*
+          * Check response
+          */
+         assertNotNull(floatingIPs);
+         assertEquals(floatingIPs.size(), 4);
+         assertEquals(floatingIPs.get(0).getId(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7");
+         assertEquals(floatingIPs.get(3).getId(), "61cea855-49cb-4846-997d-801b70c71bdd2");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testGetFloatingIP() throws IOException, InterruptedException, URISyntaxException {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(
+            new MockResponse().setResponseCode(201).setBody(stringFromResource("/floatingip_get_response.json"))));
+
+      try {
+         NeutronApi neutronApi = api(server.getUrl("/").toString(), "openstack-neutron", overrides);
+         FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne");
+
+         FloatingIP floatingIP = api.get("12345");
+
+         /*
+          * Check request
+          */
+         assertEquals(server.getRequestCount(), 2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "GET", uriApiVersion + "/floatingips/12345");
+
+         /*
+          * Check response
+          */
+         assertNotNull(floatingIP);
+         assertEquals(floatingIP.getId(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7");
+         assertEquals(floatingIP.getPortId(), "ce705c24-c1ef-408a-bda3-7bbd946164ab");
+         assertEquals(floatingIP.getFloatingIpAddress(), "172.24.4.228");
+         assertEquals(floatingIP.getFixedIpAddress(), "10.0.0.3");
+         assertEquals(floatingIP.getFloatingNetworkId(), "376da547-b977-4cfe-9cba-275c80debf57");
+         assertEquals(floatingIP.getRouterId(), "d23abc8d-2991-4a55-ba98-2aaea84cc72f");
+         assertEquals(floatingIP.getTenantId(), "4969c491a3c74ee4af974e6d800c62de");
+
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testUpdateFloatingIP() throws IOException, InterruptedException, URISyntaxException {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(
+            new MockResponse().setResponseCode(201).setBody(stringFromResource("/floatingip_update_response.json"))));
+
+      try {
+         NeutronApi neutronApi = api(server.getUrl("/").toString(), "openstack-neutron", overrides);
+         FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne");
+
+         FloatingIP.UpdateFloatingIP updateFloatingIP = FloatingIP.updateBuilder()
+               .portId("fc861431-0e6c-4842-a0ed-e2363f9bc3a8")
+               .build();
+
+         FloatingIP floatingIP = api.update("12345", updateFloatingIP);
+
+         /*
+          * Check request
+          */
+         assertEquals(server.getRequestCount(), 2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "PUT", uriApiVersion + "/floatingips/12345", "/floatingip_update_request.json");
+
+         /*
+          * Check response
+          */
+         assertNotNull(floatingIP);
+         assertEquals(floatingIP.getPortId(), "fc861431-0e6c-4842-a0ed-e2363f9bc3a8");
+
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testUpdateFloatingIPDissociate() throws IOException, InterruptedException, URISyntaxException {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(
+            new MockResponse().setResponseCode(201).setBody(stringFromResource("/floatingip_update_dissociate_response.json"))));
+
+      try {
+         NeutronApi neutronApi = api(server.getUrl("/").toString(), "openstack-neutron", overrides);
+         FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne");
+
+         FloatingIP.UpdateFloatingIP updateFloatingIP = FloatingIP.updateBuilder().build();
+
+         FloatingIP floatingIP = api.update("12345", updateFloatingIP);
+
+         /*
+          * Check request
+          */
+         assertEquals(server.getRequestCount(), 2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "PUT", uriApiVersion + "/floatingips/12345", "/floatingip_update_dissociate_request.json");
+
+         /*
+          * Check response
+          */
+         assertNotNull(floatingIP);
+         assertNull(floatingIP.getPortId());
+
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void testDeleteFloatingIP() throws IOException, InterruptedException, URISyntaxException {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(
+            new MockResponse().setResponseCode(201)));
+
+      try {
+         NeutronApi neutronApi = api(server.getUrl("/").toString(), "openstack-neutron", overrides);
+         FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne");
+
+         boolean result = api.delete("12345");
+
+         /*
+          * Check request
+          */
+         assertEquals(server.getRequestCount(), 2);
+         assertAuthentication(server);
+         assertRequest(server.takeRequest(), "DELETE", uriApiVersion + "/floatingips/12345");
+
+         /*
+          * Check response
+          */
+         assertTrue(result);
+      } finally {
+         server.shutdown();
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java
index 4402d47..867f260 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java
@@ -71,7 +71,7 @@ import org.jclouds.openstack.nova.v2_0.compute.loaders.FindSecurityGroupOrCreate
 import org.jclouds.openstack.nova.v2_0.compute.loaders.LoadFloatingIpsForInstance;
 import org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions;
 import org.jclouds.openstack.nova.v2_0.compute.strategy.ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet;
-import org.jclouds.openstack.nova.v2_0.domain.FloatingIP;
+import org.jclouds.openstack.nova.v2_0.domain.FloatingIpForServer;
 import org.jclouds.openstack.nova.v2_0.domain.Server;
 import org.jclouds.openstack.nova.v2_0.domain.Server.Status;
 import org.jclouds.openstack.nova.v2_0.domain.regionscoped.FlavorInRegion;
@@ -137,7 +137,7 @@ public class NovaComputeServiceContextModule extends
 
       bind(TemplateOptions.class).to(NovaTemplateOptions.class);
 
-      bind(new TypeLiteral<CacheLoader<RegionAndId, Iterable<? extends FloatingIP>>>() {
+      bind(new TypeLiteral<CacheLoader<RegionAndId, Iterable<? extends FloatingIpForServer>>>() {
       }).annotatedWith(Names.named("FLOATINGIP")).to(LoadFloatingIpsForInstance.class);
 
       bind(new TypeLiteral<Function<RegionSecurityGroupNameAndPorts, SecurityGroup>>() {
@@ -214,8 +214,8 @@ public class NovaComputeServiceContextModule extends
    @Provides
    @Singleton
    @Named("FLOATINGIP")
-   protected final LoadingCache<RegionAndId, Iterable<? extends FloatingIP>> instanceToFloatingIps(
-            @Named("FLOATINGIP") CacheLoader<RegionAndId, Iterable<? extends FloatingIP>> in) {
+   protected final LoadingCache<RegionAndId, Iterable<? extends FloatingIpForServer>> instanceToFloatingIps(
+            @Named("FLOATINGIP") CacheLoader<RegionAndId, Iterable<? extends FloatingIpForServer>> in) {
       return CacheBuilder.newBuilder().build(in);
    }
 
@@ -313,7 +313,7 @@ public class NovaComputeServiceContextModule extends
 
       @Override
       public boolean apply(RegionAndId regionAndId) {
-         checkNotNull(regionAndId, "regionAndId");
+         checkNotNull(regionAndId, "serverId");
          Server server = api.getServerApi(regionAndId.getRegion()).get(regionAndId.getId());
          if (server == null) {
             throw new IllegalStateException(String.format("Server %s not found.", regionAndId.getId()));
@@ -333,7 +333,7 @@ public class NovaComputeServiceContextModule extends
 
       @Override
       public boolean apply(RegionAndId regionAndId) {
-         checkNotNull(regionAndId, "regionAndId");
+         checkNotNull(regionAndId, "serverId");
          Server server = api.getServerApi(regionAndId.getRegion()).get(regionAndId.getId());
          return server == null;
       }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/50ae6828/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/AllocateAndAddFloatingIpToNode.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/AllocateAndAddFloatingIpToNode.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/AllocateAndAddFloatingIpToNode.java
index ed41190..80663ab 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/AllocateAndAddFloatingIpToNode.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/AllocateAndAddFloatingIpToNode.java
@@ -25,19 +25,29 @@ import java.util.List;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 
+import javax.annotation.Nullable;
 import javax.annotation.Resource;
-import javax.inject.Inject;
 import javax.inject.Named;
 
+import com.google.common.collect.Sets;
+import org.jclouds.Context;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.NodeMetadataBuilder;
 import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.logging.Logger;
+import org.jclouds.openstack.neutron.v2.NeutronApi;
+import org.jclouds.openstack.neutron.v2.domain.Network;
+import org.jclouds.openstack.neutron.v2.domain.Networks;
+import org.jclouds.openstack.neutron.v2.domain.Port;
+import org.jclouds.openstack.neutron.v2.features.NetworkApi;
+import org.jclouds.openstack.neutron.v2.features.PortApi;
 import org.jclouds.openstack.nova.v2_0.NovaApi;
 import org.jclouds.openstack.nova.v2_0.compute.options.NodeAndNovaTemplateOptions;
 import org.jclouds.openstack.nova.v2_0.domain.FloatingIP;
+import org.jclouds.openstack.nova.v2_0.domain.FloatingIpForServer;
 import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndId;
 import org.jclouds.openstack.nova.v2_0.extensions.FloatingIPApi;
+import org.jclouds.rest.ApiContext;
 import org.jclouds.rest.InsufficientResourcesException;
 import org.jclouds.rest.ResourceNotFoundException;
 
@@ -45,30 +55,38 @@ import com.google.common.base.Function;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
+import com.google.inject.Inject;
 
 /**
  * A function for adding and allocating an ip to a node
  */
-public class AllocateAndAddFloatingIpToNode implements
-         Function<AtomicReference<NodeAndNovaTemplateOptions>, AtomicReference<NodeMetadata>> {
+public class AllocateAndAddFloatingIpToNode
+      implements Function<AtomicReference<NodeAndNovaTemplateOptions>, AtomicReference<NodeMetadata>> {
 
    @Resource
    @Named(ComputeServiceConstants.COMPUTE_LOGGER)
    protected Logger logger = Logger.NULL;
 
+   @Inject(optional = true)
+   @Named("openstack-neutron")
+   private Supplier<Context> neutronContextSupplier;
+
    private final Predicate<AtomicReference<NodeMetadata>> nodeRunning;
    private final NovaApi novaApi;
-   private final LoadingCache<RegionAndId, Iterable<? extends FloatingIP>> floatingIpCache;
+   private final LoadingCache<RegionAndId, Iterable<? extends FloatingIpForServer>> floatingIpCache;
    private final CleanupResources cleanupResources;
 
    @Inject
-   public AllocateAndAddFloatingIpToNode(@Named(TIMEOUT_NODE_RUNNING) Predicate<AtomicReference<NodeMetadata>> nodeRunning,
-            NovaApi novaApi, @Named("FLOATINGIP") LoadingCache<RegionAndId, Iterable<? extends FloatingIP>> floatingIpCache, CleanupResources cleanupResources) {
+   public AllocateAndAddFloatingIpToNode(
+         @Named(TIMEOUT_NODE_RUNNING) Predicate<AtomicReference<NodeMetadata>> nodeRunning, NovaApi novaApi,
+         @Named("FLOATINGIP") LoadingCache<RegionAndId, Iterable<? extends FloatingIpForServer>> floatingIpCache,
+         CleanupResources cleanupResources) {
       this.nodeRunning = checkNotNull(nodeRunning, "nodeRunning");
       this.novaApi = checkNotNull(novaApi, "novaApi");
       this.floatingIpCache = checkNotNull(floatingIpCache, "floatingIpCache");
@@ -78,37 +96,80 @@ public class AllocateAndAddFloatingIpToNode implements
    @Override
    public AtomicReference<NodeMetadata> apply(AtomicReference<NodeAndNovaTemplateOptions> input) {
       checkState(nodeRunning.apply(input.get().getNodeMetadata()), "node never achieved state running %s", input.get().getNodeMetadata());
-      NodeMetadata node = input.get().getNodeMetadata().get();
+      final NodeMetadata node = input.get().getNodeMetadata().get();
       // node's location is a host
       String regionId = node.getLocation().getParent().getId();
-      FloatingIPApi floatingIpApi = novaApi.getFloatingIPApi(regionId).get();
       Optional<Set<String>> poolNames = input.get().getNovaTemplateOptions().get().getFloatingIpPoolNames();
 
-      Optional<FloatingIP> ip = allocateFloatingIPForNode(floatingIpApi, poolNames, node.getId());
-      if (!ip.isPresent()) {
-         cleanupResources.apply(node);
-         throw new InsufficientResourcesException("Failed to allocate a FloatingIP for node(" + node.getId() + ")");
-      }
-      logger.debug(">> adding floatingIp(%s) to node(%s)", ip.get().getIp(), node.getId());
+      String availabilityZone = getAvailabilityZoneFromTemplateOptionsOrDefault(input, regionId);
+
+      if (isNeutronLinked()) {
+         org.jclouds.openstack.neutron.v2.features.FloatingIPApi neutronFloatingApi = getFloatingIPApi(regionId);
+            final Optional<Port> optionalPort = getPortApi(regionId).list().concat().firstMatch(new Predicate<Port>() {
+               @Override
+               public boolean apply(@Nullable Port input) {
+                  return input.getDeviceId().equals(node.getProviderId());
+               }
+            });
+            if (optionalPort.isPresent()) {
+               Optional<org.jclouds.openstack.neutron.v2.domain.FloatingIP> floatingIPOptional = tryFindExistingFloatingIp(neutronFloatingApi, availabilityZone);
+               org.jclouds.openstack.neutron.v2.domain.FloatingIP floatingIP;
+               if (floatingIPOptional.isPresent()) {
+                  floatingIP = floatingIPOptional.get();
+               } else {
+                  floatingIP = createFloatingIpUsingNeutron(neutronFloatingApi, node, poolNames, availabilityZone);
+               }
+
+               org.jclouds.openstack.neutron.v2.domain.FloatingIP ip = neutronFloatingApi.update(floatingIP.getId(),
+                       org.jclouds.openstack.neutron.v2.domain.FloatingIP.UpdateFloatingIP
+                               .updateBuilder()
+                               .portId(optionalPort.get().getId())
+                               .build());
 
-      floatingIpApi.addToServer(ip.get().getIp(), node.getProviderId());
+               input.get().getNodeMetadata().set(NodeMetadataBuilder.fromNodeMetadata(node).publicAddresses(ImmutableSet.of(ip.getFloatingIpAddress())).build());
+            } else {
+               logger.error("Node %s doesn't have a port to attach a floating IP", node);
+               throw new IllegalStateException("Missing required port in node: " + node);
+            }
+      } else { // try nova
+         FloatingIPApi floatingIpApi = novaApi.getFloatingIPApi(regionId).get();
+
+         Optional<FloatingIP> ip = allocateFloatingIPForNodeOnNova(floatingIpApi, poolNames, node.getId());
+         if (!ip.isPresent()) {
+            cleanupResources.apply(node);
+            throw new InsufficientResourcesException("Failed to allocate a FloatingIP for node(" + node.getId() + ")");
+         }
+         logger.debug(">> adding floatingIp(%s) to node(%s)", ip.get().getIp(), node.getId());
 
-      input.get().getNodeMetadata().set(NodeMetadataBuilder.fromNodeMetadata(node).publicAddresses(ImmutableSet.of(ip.get().getIp())).build());
-      floatingIpCache.asMap().putIfAbsent(RegionAndId.fromSlashEncoded(node.getId()), ImmutableList.of(ip.get()));
+         floatingIpApi.addToServer(ip.get().getIp(), node.getProviderId());
+         input.get().getNodeMetadata().set(NodeMetadataBuilder.fromNodeMetadata(node).publicAddresses(ImmutableSet.of(ip.get().getIp())).build());
+         floatingIpCache.asMap().put(RegionAndId.fromSlashEncoded(node.getId()), ImmutableList.of(FloatingIpForServer.create(RegionAndId.fromSlashEncoded(node.getId()), ip.get().getId(), ip.get().getIp())));
+      }
       return input.get().getNodeMetadata();
    }
 
+   private String getAvailabilityZoneFromTemplateOptionsOrDefault(AtomicReference<NodeAndNovaTemplateOptions> input, String regionId) {
+      return MoreObjects.firstNonNull(input.get().getNovaTemplateOptions().get().getAvailabilityZone(),
+              Iterables.get(novaApi.getAvailabilityZoneApi(regionId).get().listAvailabilityZones(), 0).getName());
+   }
+
    /**
     * Allocates a FloatingIP for a given Node
     *
-    * @param floatingIpApi FloatingIPApi to create or query for a valid FloatingIP
-    * @param poolNames optional set of pool names from which we will attempt to allocate an IP from. Most cases this is null
-    * @param nodeID optional id of the Node we are trying to allocate a FloatingIP for. Used here only for logging purposes
+    * @param floatingIpApi
+    *           FloatingIPApi to create or query for a valid FloatingIP
+    * @param poolNames
+    *           optional set of pool names from which we will attempt to allocate
+    *           an IP from. Most cases this is null
+    * @param nodeID
+    *           optional id of the Node we are trying to allocate a FloatingIP for.
+    *           Used here only for logging purposes
     * @return Optional<FloatingIP>
     */
-   private synchronized Optional<FloatingIP> allocateFloatingIPForNode(FloatingIPApi floatingIpApi, Optional<Set<String>> poolNames, String nodeID) {
+   private synchronized Optional<FloatingIP> allocateFloatingIPForNodeOnNova(FloatingIPApi floatingIpApi,
+         Optional<Set<String>> poolNames, String nodeID) {
 
-      FloatingIP ip = null;
+      FloatingIP ip;
 
       // 1.) Attempt to allocate from optionally passed poolNames
       if (poolNames.isPresent()) {
@@ -118,9 +179,11 @@ public class AllocateAndAddFloatingIpToNode implements
                ip = floatingIpApi.allocateFromPool(poolName);
                return Optional.of(ip);
             } catch (ResourceNotFoundException ex) {
-               logger.trace("<< [%s] failed to allocate floating IP from pool %s for node(%s)", ex.getMessage(), poolName, nodeID);
+               logger.trace("<< [%s] failed to allocate floating IP from pool %s for node(%s)", ex.getMessage(),
+                     poolName, nodeID);
             } catch (InsufficientResourcesException ire) {
-               logger.trace("<< [%s] failed to allocate floating IP from pool %s for node(%s)", ire.getMessage(), poolName, nodeID);
+               logger.trace("<< [%s] failed to allocate floating IP from pool %s for node(%s)", ire.getMessage(),
+                     poolName, nodeID);
             }
          }
       }
@@ -136,17 +199,18 @@ public class AllocateAndAddFloatingIpToNode implements
          logger.trace("<< [%s] failed to create floating IP for node(%s)", ire.getMessage(), nodeID);
       }
 
-      // 3.) If no IP was found make final attempt by searching through list of available IP's
+      // 3.) If no IP was found make final attempt by searching through list of
+      // available IP's
       logger.trace(">> searching for existing, unassigned floating IP for node(%s)", nodeID);
-      List<FloatingIP> unassignedIps = Lists.newArrayList(Iterables.filter(floatingIpApi.list(),
-            new Predicate<FloatingIP>() {
+      List<FloatingIP> unassignedIps = Lists
+            .newArrayList(Iterables.filter(floatingIpApi.list(), new Predicate<FloatingIP>() {
 
                @Override
                public boolean apply(FloatingIP arg0) {
                   return arg0.getFixedIp() == null;
                }
 
-      }));
+            }));
       // try to prevent multiple parallel launches from choosing the same ip.
       if (unassignedIps.isEmpty()) {
          return Optional.absent();
@@ -156,8 +220,72 @@ public class AllocateAndAddFloatingIpToNode implements
       return Optional.fromNullable(ip);
    }
 
+   private Optional<org.jclouds.openstack.neutron.v2.domain.FloatingIP> tryFindExistingFloatingIp(org.jclouds.openstack.neutron.v2.features.FloatingIPApi neutronFloatingApi, final String availabilityZone) {
+      Optional<org.jclouds.openstack.neutron.v2.domain.FloatingIP> floatingIPOptional = neutronFloatingApi.list().concat().firstMatch(new Predicate<org.jclouds.openstack.neutron.v2.domain.FloatingIP>() {
+         @Override
+         public boolean apply(@Nullable org.jclouds.openstack.neutron.v2.domain.FloatingIP input) {
+            return input.getPortId() == null && input.getAvailabilityZone().equals(availabilityZone);
+         }
+      });
+      return floatingIPOptional;
+   }
+
+   private org.jclouds.openstack.neutron.v2.domain.FloatingIP createFloatingIpUsingNeutron(org.jclouds.openstack.neutron.v2.features.FloatingIPApi neutronFloatingApi,
+                                                                                           NodeMetadata node, Optional<Set<String>> poolNames, final String availabilityZone) {
+      String regionId = node.getLocation().getParent().getId();
+      List<Network> networks = getSuitableNetworks(regionId, availabilityZone, poolNames.or(Sets.<String>newHashSet()));
+      org.jclouds.openstack.neutron.v2.domain.FloatingIP floatingIP = null;
+      for (Network network : networks) {
+         try {
+            logger.debug(">> allocating floating IP from network %s for node(%s)", network, node);
+            org.jclouds.openstack.neutron.v2.domain.FloatingIP createFloatingIP = org.jclouds.openstack.neutron.v2.domain.FloatingIP.CreateFloatingIP.createBuilder(network.getId()).availabilityZone(network.getAvailabilityZone()).build();
+            floatingIP = neutronFloatingApi.create((org.jclouds.openstack.neutron.v2.domain.FloatingIP.CreateFloatingIP) createFloatingIP);
+            logger.debug(">> allocated floating IP(%s) from network(%s) for node(%s)", floatingIP, network, node);
+            floatingIpCache.asMap().put(RegionAndId.fromSlashEncoded(node.getId()), ImmutableList.of(FloatingIpForServer.create(RegionAndId.fromSlashEncoded(node.getId()), floatingIP.getId(), floatingIP.getFloatingIpAddress())));
+            return floatingIP;
+         } catch (Exception ex) {
+            logger.trace("<< [%s] failed to allocate a floating IP from network %s for node(%s)", ex.getMessage(), network, node);
+         }
+      }
+
+      throw new IllegalStateException("Failed to allocate a floating IP for node " + node + ".\n" +
+            "Failed to find suitable external networks or to allocate from poolNames specified: "
+            + Iterables.toString(poolNames.get()));
+
+   }
+
+
+   /**
+    * Get all suitable networks to allocate a floating ip
+    *
+    * It will prefer networks specified using the poolNames first and then the external networks in the given availability zone
+    */
+   private List<Network> getSuitableNetworks(String regionId, final String availabilityZone, final Set<String> poolNames) {
+      List<Network> allNetworks = getNetworkApi(regionId).list().concat().toList();
+      Iterable<Network> externalNetworks = Iterables.filter(allNetworks, Networks.Predicates.externalNetworks(availabilityZone));
+      Iterable<Network> networksFromPoolName = Iterables.filter(allNetworks, Networks.Predicates.namedNetworks(poolNames));
+      return Lists.newArrayList(Iterables.concat(networksFromPoolName, externalNetworks));
+   }
+
+   private boolean isNeutronLinked() {
+      return neutronContextSupplier != null && neutronContextSupplier.get() != null;
+   }
+
+   private org.jclouds.openstack.neutron.v2.features.FloatingIPApi getFloatingIPApi(String region) {
+      return ((ApiContext<NeutronApi>) neutronContextSupplier.get()).getApi().getFloatingIPApi(region);
+   }
+
+   private PortApi getPortApi(String regionId) {
+      return ((ApiContext<NeutronApi>) neutronContextSupplier.get()).getApi().getPortApi(regionId);
+   }
+
+   private NetworkApi getNetworkApi(String regionId) {
+      return ((ApiContext<NeutronApi>) neutronContextSupplier.get()).getApi().getNetworkApi(regionId);
+   }
+
    @Override
    public String toString() {
       return MoreObjects.toStringHelper("AllocateAndAddFloatingIpToNode").toString();
    }
+
 }