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 97530f87df..e7863d2a70 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; @@ -88,6 +88,12 @@ public interface NeutronApi extends Closeable { @Delegate SecurityGroupApi getSecurityGroupApi(@EndpointParam(parser = VersionAwareRegionToEndpoint.class) String region); + /** + * Provides access to Floating IP features. + */ + @Delegate + FloatingIPApi getFloatingIPApi(@EndpointParam(parser = VersionAwareRegionToEndpoint.class) String region); + /** * Provides access to Router features. * @@ -98,16 +104,6 @@ public interface NeutronApi extends Closeable { @Delegate Optional getRouterApi(@EndpointParam(parser = VersionAwareRegionToEndpoint.class) String region); - /** - * Provides access to Floating IP features. - * - *

NOTE

- * 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 getFloatingIPApi(@EndpointParam(parser = VersionAwareRegionToEndpoint.class) String region); - /** * Provides access to LBaaS features. * 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 be61ad3cca..dbb550cf1c 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); } /** @@ -88,6 +94,14 @@ public class FloatingIP { return routerId; } + /** + * @return the project id of the Floating IP + */ + @Nullable + public String getProjectId() { + return projectId; + } + /** * @return the tenant id of the Floating IP */ @@ -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(); } @@ -191,6 +218,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 @@ -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 { 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 c2695930d0..d8fca09169 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 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 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, @@ -205,6 +209,14 @@ public class Network { return tenantId; } + /** + * @return the availability zone of the Network + */ + @Nullable + public String getAvailabilityZone() { + return availabilityZone; + } + /** * @return the networkType of the Network */ @@ -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) @@ -436,6 +450,17 @@ public class Network { return self(); } + /** + * 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. * 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 9b3bf104ca..f47e13ba03 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 { protected Networks(Iterable networks, Iterable networksLinks) { super(networks, networksLinks); } + + public static class Predicates { + + public static Predicate externalNetworks(final String availabilityZone) { + return new Predicate() { + @Override + public boolean apply(Network network) { + return availabilityZone.equals(network.getAvailabilityZone()) && network.getExternal(); + } + }; + } + + public static Predicate namedNetworks(final Set names) { + return new Predicate() { + @Override + public boolean apply(Network network) { + return names.contains(network.getName()); + } + }; + } + } } 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/features/FloatingIPApi.java similarity index 94% rename from apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/extensions/FloatingIPApi.java rename to apis/openstack-neutron/src/main/java/org/jclouds/openstack/neutron/v2/features/FloatingIPApi.java index a00bc042a5..43d7b3ca8c 100644 --- 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/features/FloatingIPApi.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jclouds.openstack.neutron.v2.extensions; +package org.jclouds.openstack.neutron.v2.features; import javax.inject.Named; import javax.ws.rs.Consumes; @@ -37,9 +37,7 @@ 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; @@ -61,7 +59,6 @@ import com.google.common.annotations.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 { /** 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 4d54d1fc5f..058ea3f746 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> markerToNextForArg0(Optional 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>() { @SuppressWarnings("unchecked") 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 ae84c36154..595b22fdff 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 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"); 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/features/FloatingIPApiLiveTest.java similarity index 57% rename from apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/extensions/FloatingIPApiLiveTest.java rename to apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/features/FloatingIPApiLiveTest.java index a3d5f3d952..a96088476a 100644 --- 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/features/FloatingIPApiLiveTest.java @@ -15,28 +15,25 @@ * limitations under the License. */ -package org.jclouds.openstack.neutron.v2.extensions; +package org.jclouds.openstack.neutron.v2.features; -import static org.testng.Assert.assertEquals; 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.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.Assert; import org.testng.annotations.Test; import com.google.common.collect.ImmutableSet; /** - * Tests parsing and Guice wiring of RouterApi + * Tests parsing and Guice wiring of FloatingIPApi */ @Test(groups = "live", testName = "FloatingIPApiLiveTest") public class FloatingIPApiLiveTest extends BaseNeutronApiLiveTest { @@ -44,44 +41,32 @@ public class FloatingIPApiLiveTest extends BaseNeutronApiLiveTest { public void testCreateUpdateAndDeleteFloatingIP() { for (String region : api.getConfiguredRegions()) { - SubnetApi subnetApi = api.getSubnetApi(region); - FloatingIPApi floatingIPApi = api.getFloatingIPApi(region).get(); + FloatingIPApi floatingIPApi = api.getFloatingIPApi(region); NetworkApi networkApi = api.getNetworkApi(region); FloatingIP floatingIPGet = null; - String ipv4SubnetId = null; - Network network = null; + Network network; try { - network = networkApi.create( - Network.createBuilder("jclouds-network-test").external(true).networkType(NetworkType.LOCAL).build()); - assertNotNull(network); + network = networkApi.list().concat().firstMatch(new Predicate() { + @Override + public boolean apply(Network input) { + return input.getExternal(); + } + }).orNull(); - 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()); + 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 floatingIPs = floatingIPApi.list().concat().toSet(); - FloatingIP floatingIPList = floatingIPs.iterator().next(); - floatingIPGet = floatingIPApi.get(floatingIPList.getId()); + floatingIPGet = floatingIPApi.get(floatingIP.getId()); assertNotNull(floatingIPGet); - assertEquals(floatingIPGet, floatingIPList); + assertTrue(floatingIPs.contains(floatingIP)); } finally { - try { - assertTrue(floatingIPApi.delete(floatingIPGet.getId())); - } - finally { - try { - assertTrue(subnetApi.delete(ipv4SubnetId)); - } - finally { - assertTrue(networkApi.delete(network.getId())); - } - } + assertTrue(floatingIPApi.delete(floatingIPGet.getId())); } } } 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/features/FloatingIPApiMockTest.java similarity index 87% rename from apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/extensions/FloatingIPApiMockTest.java rename to apis/openstack-neutron/src/test/java/org/jclouds/openstack/neutron/v2/features/FloatingIPApiMockTest.java index ea368ee68a..b34d8ab7cf 100644 --- 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/features/FloatingIPApiMockTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jclouds.openstack.neutron.v2.extensions; +package org.jclouds.openstack.neutron.v2.features; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; @@ -45,13 +45,12 @@ 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(); + FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne"); FloatingIP.CreateFloatingIP createFip = FloatingIP.createBuilder("376da547-b977-4cfe-9cba-275c80debf57") .portId("ce705c24-c1ef-408a-bda3-7bbd946164ab") @@ -62,9 +61,8 @@ public class FloatingIPApiMockTest extends BaseNeutronApiMockTest { /* * Check request */ - assertEquals(server.getRequestCount(), 3); + assertEquals(server.getRequestCount(), 2); assertAuthentication(server); - assertExtensions(server, uriApiVersion + ""); assertRequest(server.takeRequest(), "POST", uriApiVersion + "/floatingips", "/floatingip_create_request.json"); /* @@ -87,21 +85,19 @@ public class FloatingIPApiMockTest extends BaseNeutronApiMockTest { 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(); + FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne"); FloatingIPs floatingIPs = api.list(PaginationOptions.Builder.limit(2).marker("abcdefg")); /* * Check request */ - assertEquals(server.getRequestCount(), 3); + assertEquals(server.getRequestCount(), 2); assertAuthentication(server); - assertExtensions(server, uriApiVersion + ""); assertRequest(server.takeRequest(), "GET", uriApiVersion + "/floatingips?limit=2&marker=abcdefg"); /* @@ -119,13 +115,12 @@ public class FloatingIPApiMockTest extends BaseNeutronApiMockTest { 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(); + FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne"); // Note: Lazy! Have to actually look at the collection. List floatingIPs = api.list().concat().toList(); @@ -133,9 +128,8 @@ public class FloatingIPApiMockTest 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"); @@ -154,22 +148,20 @@ public class FloatingIPApiMockTest extends BaseNeutronApiMockTest { 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(); + FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne"); FloatingIP floatingIP = api.get("12345"); /* * Check request */ - assertEquals(server.getRequestCount(), 3); + assertEquals(server.getRequestCount(), 2); assertAuthentication(server); - assertExtensions(server, uriApiVersion + ""); assertRequest(server.takeRequest(), "GET", uriApiVersion + "/floatingips/12345"); /* @@ -192,13 +184,12 @@ public class FloatingIPApiMockTest extends BaseNeutronApiMockTest { 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(); + FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne"); FloatingIP.UpdateFloatingIP updateFloatingIP = FloatingIP.updateBuilder() .portId("fc861431-0e6c-4842-a0ed-e2363f9bc3a8") @@ -209,9 +200,8 @@ public class FloatingIPApiMockTest extends BaseNeutronApiMockTest { /* * Check request */ - assertEquals(server.getRequestCount(), 3); + assertEquals(server.getRequestCount(), 2); assertAuthentication(server); - assertExtensions(server, uriApiVersion + ""); assertRequest(server.takeRequest(), "PUT", uriApiVersion + "/floatingips/12345", "/floatingip_update_request.json"); /* @@ -228,13 +218,12 @@ public class FloatingIPApiMockTest extends BaseNeutronApiMockTest { 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(); + FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne"); FloatingIP.UpdateFloatingIP updateFloatingIP = FloatingIP.updateBuilder().build(); @@ -243,9 +232,8 @@ public class FloatingIPApiMockTest extends BaseNeutronApiMockTest { /* * Check request */ - assertEquals(server.getRequestCount(), 3); + assertEquals(server.getRequestCount(), 2); assertAuthentication(server); - assertExtensions(server, uriApiVersion + ""); assertRequest(server.takeRequest(), "PUT", uriApiVersion + "/floatingips/12345", "/floatingip_update_dissociate_request.json"); /* @@ -262,22 +250,20 @@ public class FloatingIPApiMockTest extends BaseNeutronApiMockTest { 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(); + FloatingIPApi api = neutronApi.getFloatingIPApi("RegionOne"); boolean result = api.delete("12345"); /* * Check request */ - assertEquals(server.getRequestCount(), 3); + assertEquals(server.getRequestCount(), 2); assertAuthentication(server); - assertExtensions(server, uriApiVersion + ""); assertRequest(server.takeRequest(), "DELETE", uriApiVersion + "/floatingips/12345"); /* 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 4402d479fa..867f260253 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>>() { + bind(new TypeLiteral>>() { }).annotatedWith(Names.named("FLOATINGIP")).to(LoadFloatingIpsForInstance.class); bind(new TypeLiteral>() { @@ -214,8 +214,8 @@ public class NovaComputeServiceContextModule extends @Provides @Singleton @Named("FLOATINGIP") - protected final LoadingCache> instanceToFloatingIps( - @Named("FLOATINGIP") CacheLoader> in) { + protected final LoadingCache> instanceToFloatingIps( + @Named("FLOATINGIP") CacheLoader> 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; } 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 ed41190f29..80663ab16a 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> { +public class AllocateAndAddFloatingIpToNode + implements Function, AtomicReference> { @Resource @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; + @Inject(optional = true) + @Named("openstack-neutron") + private Supplier neutronContextSupplier; + private final Predicate> nodeRunning; private final NovaApi novaApi; - private final LoadingCache> floatingIpCache; + private final LoadingCache> floatingIpCache; private final CleanupResources cleanupResources; @Inject - public AllocateAndAddFloatingIpToNode(@Named(TIMEOUT_NODE_RUNNING) Predicate> nodeRunning, - NovaApi novaApi, @Named("FLOATINGIP") LoadingCache> floatingIpCache, CleanupResources cleanupResources) { + public AllocateAndAddFloatingIpToNode( + @Named(TIMEOUT_NODE_RUNNING) Predicate> nodeRunning, NovaApi novaApi, + @Named("FLOATINGIP") LoadingCache> 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 apply(AtomicReference 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> poolNames = input.get().getNovaTemplateOptions().get().getFloatingIpPoolNames(); - Optional ip = allocateFloatingIPForNode(floatingIpApi, poolNames, node.getId()); - if (!ip.isPresent()) { - cleanupResources.apply(node); - throw new InsufficientResourcesException("Failed to allocate a FloatingIP for node(" + node.getId() + ")"); + String availabilityZone = getAvailabilityZoneFromTemplateOptionsOrDefault(input, regionId); + + if (isNeutronLinked()) { + org.jclouds.openstack.neutron.v2.features.FloatingIPApi neutronFloatingApi = getFloatingIPApi(regionId); + final Optional optionalPort = getPortApi(regionId).list().concat().firstMatch(new Predicate() { + @Override + public boolean apply(@Nullable Port input) { + return input.getDeviceId().equals(node.getProviderId()); + } + }); + if (optionalPort.isPresent()) { + Optional 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()); + + 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 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()); + + 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()))); } - logger.debug(">> adding floatingIp(%s) to node(%s)", ip.get().getIp(), node.getId()); - - floatingIpApi.addToServer(ip.get().getIp(), node.getProviderId()); - - 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())); return input.get().getNodeMetadata(); } + private String getAvailabilityZoneFromTemplateOptionsOrDefault(AtomicReference 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 */ - private synchronized Optional allocateFloatingIPForNode(FloatingIPApi floatingIpApi, Optional> poolNames, String nodeID) { + private synchronized Optional allocateFloatingIPForNodeOnNova(FloatingIPApi floatingIpApi, + Optional> 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 unassignedIps = Lists.newArrayList(Iterables.filter(floatingIpApi.list(), - new Predicate() { + List unassignedIps = Lists + .newArrayList(Iterables.filter(floatingIpApi.list(), new Predicate() { @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 tryFindExistingFloatingIp(org.jclouds.openstack.neutron.v2.features.FloatingIPApi neutronFloatingApi, final String availabilityZone) { + Optional floatingIPOptional = neutronFloatingApi.list().concat().firstMatch(new Predicate() { + @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> poolNames, final String availabilityZone) { + String regionId = node.getLocation().getParent().getId(); + List networks = getSuitableNetworks(regionId, availabilityZone, poolNames.or(Sets.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 getSuitableNetworks(String regionId, final String availabilityZone, final Set poolNames) { + List allNetworks = getNetworkApi(regionId).list().concat().toList(); + Iterable externalNetworks = Iterables.filter(allNetworks, Networks.Predicates.externalNetworks(availabilityZone)); + Iterable 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) neutronContextSupplier.get()).getApi().getFloatingIPApi(region); + } + + private PortApi getPortApi(String regionId) { + return ((ApiContext) neutronContextSupplier.get()).getApi().getPortApi(regionId); + } + + private NetworkApi getNetworkApi(String regionId) { + return ((ApiContext) neutronContextSupplier.get()).getApi().getNetworkApi(regionId); + } + @Override public String toString() { return MoreObjects.toStringHelper("AllocateAndAddFloatingIpToNode").toString(); } + } 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 5da0b87160..3e215bb549 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 { public boolean removeSecurityGroupCreatedByJcloudsAndInvalidateCache(Set tags) { String securityGroupIdCreatedByJclouds = getSecurityGroupIdCreatedByJclouds(tags); + if (securityGroupIdCreatedByJclouds == null) return true; return securityGroupExtension.removeSecurityGroup(securityGroupIdCreatedByJclouds); } 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 94309c8655..8161443a50 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 neutronContextSupplier; + private final NovaApi novaApi; - private final LoadingCache> floatingIpCache; + private final LoadingCache> floatingIpCache; @Inject public RemoveFloatingIpFromNodeAndDeallocate(NovaApi novaApi, - @Named("FLOATINGIP") LoadingCache> floatingIpCache) { + @Named("FLOATINGIP") LoadingCache> 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) neutronContextSupplier.get()).getApi().getFloatingIPApi(region); + } + @Override public String toString() { - return MoreObjects.toStringHelper("RemoveFloatingIpFromNodeAndDecreate").toString(); + return MoreObjects.toStringHelper("RemoveFloatingIpFromNodeAndDeallocate").toString(); } } 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 1487a13372..2e404d5762 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> { +public class LoadFloatingIpsForInstance extends CacheLoader> { private final NovaApi api; @Inject @@ -44,7 +46,7 @@ public class LoadFloatingIpsForInstance extends CacheLoader load(final RegionAndId key) throws Exception { + public Iterable load(final RegionAndId key) throws Exception { String region = key.getRegion(); Optional ipApiOptional = api.getFloatingIPApi(region); if (ipApiOptional.isPresent()) { @@ -54,7 +56,13 @@ public class LoadFloatingIpsForInstance extends CacheLoader() { + @Override + public FloatingIpForServer apply(FloatingIP input) { + return FloatingIpForServer.create(key, input.getId(), input.getIp()); + } + }); } return ImmutableSet.of(); } 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 0000000000..784ebfcdbf --- /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() { + } +} 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 0000000000..49310164ba --- /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 = 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 fixedIPS(); + + @SerializedNames({"port_id", "net_id", "port_state", "fixed_ips"}) + public static PortInterface create(String portId, String netId, State portState, List fixedIPS) { + return new AutoValue_PortInterface(portId, netId, portState, + fixedIPS == null ? ImmutableList. of() : ImmutableList.copyOf(fixedIPS) + ); + } + + PortInterface() {} + +} 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 512f4a04db..555d4e431d 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 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 listPortInterfaces(@PathParam("id") String id); + } 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 1b6c6adb54..d4aac073e3 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 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()); } } 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 0000000000..1559910502 --- /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.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 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; + } +} 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 af20d8508c..70ef37941b 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. 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. builder().put(keystoneAuthWithUsernameAndPasswordAndTenantName, - responseWithKeystoneAccess).put(extensionsOfNovaRequest, extensionsOfNovaResponse).put( - createFloatingIP, createFloatingIPResponse) - .put(addFloatingIPRequest, addFloatingIPResponse).build()).getContext().utils().injector() - .getInstance(AllocateAndAddFloatingIpToNode.class); + ImmutableMap. builder() + .put(keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess) + .put(listAZs, listAZsResponseForUnassigned) + .put(extensionsOfNovaRequest, extensionsOfNovaResponse) + .put(createFloatingIP, createFloatingIPResponse) + .put(addFloatingIPRequest, addFloatingIPResponse).build()) + .getContext().utils().injector() + .getInstance(AllocateAndAddFloatingIpToNode.class); AtomicReference nodeRef = Atomics.newReference(node); AtomicReference 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. 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. 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. builder().put(keystoneAuthWithUsernameAndPasswordAndTenantName, - responseWithKeystoneAccess).put(extensionsOfNovaRequest, extensionsOfNovaResponse).put( - createFloatingIP, createFloatingIPResponse) - .put(addFloatingIPRequest, addFloatingIPResponse).put(list, - listResponseForUnassigned).build()).getContext().utils().injector() - .getInstance(AllocateAndAddFloatingIpToNode.class); + ImmutableMap. 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 nodeRef = Atomics.newReference(node); AtomicReference 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. 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. 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. builder().put(keystoneAuthWithUsernameAndPasswordAndTenantName, - responseWithKeystoneAccess).put(extensionsOfNovaRequest, extensionsOfNovaResponse).put( - createFloatingIP, createFloatingIPResponse) - .put(addFloatingIPRequest, addFloatingIPResponse).put(list, - listResponseForUnassigned).build()).getContext().utils().injector() - .getInstance(AllocateAndAddFloatingIpToNode.class); + ImmutableMap. 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 nodeRef = Atomics.newReference(node); AtomicReference optionsRef = Atomics.newReference(options); 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 84d2fa7c88..6760c6d7df 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); 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 0000000000..0058762dfb --- /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 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 615f0cf7aa..5aa88da8a3 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 *