[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 <[email protected]> Authored: Wed Feb 14 16:03:57 2018 +0100 Committer: Andrea Turli <[email protected]> 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(); } + }
