mirror of https://github.com/apache/jclouds.git
openstack-nova: wiring Floating IP functionality into ComputeServiceAdaptor and ServerToNodeMetadata
This commit is contained in:
parent
dd4d13d6bb
commit
f0d6c9f10d
|
@ -22,17 +22,19 @@ import java.util.Set;
|
|||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Optional;
|
||||
import org.jclouds.compute.ComputeServiceAdapter;
|
||||
import org.jclouds.compute.domain.Template;
|
||||
import org.jclouds.domain.Location;
|
||||
import org.jclouds.domain.LocationBuilder;
|
||||
import org.jclouds.domain.LoginCredentials;
|
||||
import org.jclouds.openstack.nova.v1_1.NovaClient;
|
||||
import org.jclouds.openstack.nova.v1_1.domain.Flavor;
|
||||
import org.jclouds.openstack.nova.v1_1.domain.Image;
|
||||
import org.jclouds.openstack.nova.v1_1.domain.RebootType;
|
||||
import org.jclouds.openstack.nova.v1_1.domain.Server;
|
||||
import org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions;
|
||||
import org.jclouds.openstack.nova.v1_1.domain.*;
|
||||
import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPClient;
|
||||
import org.jclouds.openstack.nova.v1_1.features.FlavorClient;
|
||||
import org.jclouds.openstack.nova.v1_1.features.ImageClient;
|
||||
import org.jclouds.openstack.nova.v1_1.features.ServerClient;
|
||||
|
@ -40,11 +42,14 @@ import org.jclouds.openstack.nova.v1_1.features.ServerClient;
|
|||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import org.jclouds.openstack.nova.v1_1.reference.NovaConstants;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
/**
|
||||
* The adapter used by the NovaComputeServiceContextModule to interface the
|
||||
* nova-specific domain model to the computeService generic domain model.
|
||||
*
|
||||
*
|
||||
* @author Matt Stephenson
|
||||
*/
|
||||
public class NovaComputeServiceAdapter implements ComputeServiceAdapter<Server, Flavor, Image, Location> {
|
||||
|
@ -54,6 +59,12 @@ public class NovaComputeServiceAdapter implements ComputeServiceAdapter<Server,
|
|||
private final FlavorClient defaultFlavorClient;
|
||||
private final ImageClient defaultImageClient;
|
||||
private final Set<String> regions;
|
||||
private final String defaultRegion;
|
||||
|
||||
@Inject
|
||||
@Named(NovaConstants.PROPERTY_NOVA_AUTO_ALLOCATE_FLOATING_IPS)
|
||||
@VisibleForTesting
|
||||
boolean autoAllocateFloatingIps = false;
|
||||
|
||||
@Inject
|
||||
public NovaComputeServiceAdapter(NovaClient novaClient) {
|
||||
|
@ -63,20 +74,47 @@ public class NovaComputeServiceAdapter implements ComputeServiceAdapter<Server,
|
|||
throw new IllegalStateException(
|
||||
"No regions exist for this compute service. The Nova compute service requires at least 1 region.");
|
||||
}
|
||||
String region = regions.iterator().next();
|
||||
this.defaultLocationServerClient = novaClient.getServerClientForRegion(region);
|
||||
this.defaultFlavorClient = novaClient.getFlavorClientForRegion(region);
|
||||
this.defaultImageClient = novaClient.getImageClientForRegion(region);
|
||||
this.defaultRegion = regions.iterator().next();
|
||||
this.defaultLocationServerClient = novaClient.getServerClientForRegion(defaultRegion);
|
||||
this.defaultFlavorClient = novaClient.getFlavorClientForRegion(defaultRegion);
|
||||
this.defaultImageClient = novaClient.getImageClientForRegion(defaultRegion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeAndInitialCredentials<Server> createNodeWithGroupEncodedIntoName(String tag, String name,
|
||||
Template template) {
|
||||
ServerClient serverClient = template.getLocation() != null ? novaClient.getServerClientForRegion(template
|
||||
.getLocation().getId()) : defaultLocationServerClient;
|
||||
String region = template.getLocation() == null ? defaultRegion : template.getLocation().getId();
|
||||
ServerClient serverClient = template.getLocation() != null ? novaClient.getServerClientForRegion(template.getLocation().getId()) : defaultLocationServerClient;
|
||||
// TODO: make NovaTemplateOptions with the following:
|
||||
// security group, key pair, floating ip (attach post server-create?)
|
||||
NovaTemplateOptions templateOptions = NovaTemplateOptions.class.cast(template.getOptions());
|
||||
|
||||
boolean autoAllocateFloatingIps = templateOptions.getFloatingIps().isEmpty() &&
|
||||
(this.autoAllocateFloatingIps || templateOptions.isAutoAssignFloatingIp());
|
||||
Set<String> floatingIps = templateOptions.getFloatingIps();
|
||||
FloatingIPClient floatingIPClient = null;
|
||||
|
||||
if (autoAllocateFloatingIps || !templateOptions.getFloatingIps().isEmpty()) {
|
||||
Optional<FloatingIPClient> floatingIPClientWrapper = novaClient.getFloatingIPExtensionForRegion(region);
|
||||
checkArgument(floatingIPClientWrapper.isPresent(), "Floating IP settings are required by configuration, but the extension is not available!");
|
||||
floatingIPClient = floatingIPClientWrapper.get();
|
||||
}
|
||||
|
||||
// Allocate floating ip(s)
|
||||
if (floatingIPClient != null && autoAllocateFloatingIps) {
|
||||
floatingIps.add(floatingIPClient.allocate().getId());
|
||||
}
|
||||
|
||||
Server server = serverClient.createServer(name, template.getImage().getId(), template.getHardware().getId());
|
||||
|
||||
// Attaching floating ip(s) to server
|
||||
// TODO what if we're creating n nodes in group? the user would likely expect a single floatingIp per server!
|
||||
if (floatingIPClient != null) {
|
||||
for (String floatingIp : floatingIps) {
|
||||
floatingIPClient.addFloatingIP(server.getId(), floatingIp);
|
||||
}
|
||||
}
|
||||
|
||||
return new NodeAndInitialCredentials<Server>(server, server.getId() + "", LoginCredentials.builder()
|
||||
.password(server.getAdminPass()).build());
|
||||
}
|
||||
|
|
|
@ -18,8 +18,10 @@
|
|||
*/
|
||||
package org.jclouds.openstack.nova.v1_1.compute.functions;
|
||||
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import org.jclouds.compute.domain.NodeMetadata;
|
||||
import org.jclouds.compute.domain.NodeMetadataBuilder;
|
||||
import org.jclouds.openstack.nova.v1_1.compute.domain.RegionAndName;
|
||||
import org.jclouds.openstack.nova.v1_1.domain.Address;
|
||||
import org.jclouds.openstack.nova.v1_1.domain.Server;
|
||||
|
||||
|
@ -27,23 +29,36 @@ import com.google.common.base.Function;
|
|||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* A function for transforming a nova-specific Server into a generic
|
||||
* NodeMetadata object.
|
||||
*
|
||||
*
|
||||
* @author Matt Stephenson
|
||||
*/
|
||||
public class ServerToNodeMetadata implements Function<Server, NodeMetadata> {
|
||||
private final LoadingCache<RegionAndName, Iterable<String>> floatingIpCache;
|
||||
|
||||
@Inject
|
||||
public ServerToNodeMetadata(@Named("FLOATINGIP") LoadingCache<RegionAndName, Iterable<String>> floatingIpCache) {
|
||||
this.floatingIpCache = checkNotNull(floatingIpCache, "cache");
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeMetadata apply(Server server) {
|
||||
return new NodeMetadataBuilder()
|
||||
// TODO: scope id to region, if there's a chance for conflict
|
||||
// TODO: pass correct region into floatingIpCache request
|
||||
.id(server.getId())
|
||||
.providerId(server.getId())
|
||||
.name(server.getName())
|
||||
.publicAddresses(
|
||||
Iterables.transform(server.getPublicAddresses(), new AddressToStringTransformationFunction()))
|
||||
Iterables.concat(Iterables.transform(server.getPublicAddresses(), new AddressToStringTransformationFunction()),
|
||||
floatingIpCache.getUnchecked(new RegionAndName(null, server.getId()))))
|
||||
.privateAddresses(
|
||||
Iterables.transform(server.getPrivateAddresses(), new AddressToStringTransformationFunction()))
|
||||
.state(server.getStatus().getNodeState()).userMetadata(ImmutableMap.copyOf(server.getMetadata())).build();
|
||||
|
|
|
@ -23,7 +23,9 @@ import static org.testng.Assert.assertNotNull;
|
|||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.jclouds.compute.ComputeService;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
|
@ -34,26 +36,52 @@ import com.google.common.collect.ImmutableMultimap;
|
|||
|
||||
/**
|
||||
* Tests the compute service abstraction of the nova client.
|
||||
*
|
||||
*
|
||||
* @author Matt Stephenson
|
||||
*/
|
||||
@Test(groups = "unit", testName = "NovaComputeServiceExpectTest")
|
||||
public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTest {
|
||||
|
||||
public void testListServersWhenResponseIs2xx() throws Exception {
|
||||
HttpRequest getExtensions = HttpRequest
|
||||
.builder()
|
||||
.method("GET")
|
||||
.endpoint(URI.create("https://compute.north.host/v1.1/3456/extensions"))
|
||||
.headers(
|
||||
ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
|
||||
.put("X-Auth-Token", authToken).build()).build();
|
||||
HttpRequest listFloatingIps = HttpRequest
|
||||
.builder()
|
||||
.method("GET")
|
||||
.endpoint(URI.create("https://compute.north.host/v1.1/3456/os-floating-ips"))
|
||||
.headers(
|
||||
ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
|
||||
.put("X-Auth-Token", authToken).build()).build();
|
||||
HttpRequest listServers = HttpRequest
|
||||
.builder()
|
||||
.method("GET")
|
||||
.endpoint(URI.create("https://compute.north.host/v1.1/3456/servers/detail"))
|
||||
.headers(
|
||||
ImmutableMultimap.<String, String> builder().put("Accept", "application/json")
|
||||
ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
|
||||
.put("X-Auth-Token", authToken).build()).build();
|
||||
|
||||
|
||||
HttpResponse getExtensionsResponse = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/extension_list_normal.json")).build();
|
||||
|
||||
HttpResponse listFloatingIpsResponse = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/floatingip_list.json")).build();
|
||||
|
||||
HttpResponse listServersResponse = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/server_list_details.json")).build();
|
||||
|
||||
ComputeService clientWhenServersExist = requestsSendResponses(keystoneAuthWithAccessKeyAndSecretKey,
|
||||
responseWithKeystoneAccess, listServers, listServersResponse);
|
||||
Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.of(
|
||||
keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess,
|
||||
listFloatingIps, listFloatingIpsResponse,
|
||||
getExtensions, getExtensionsResponse,
|
||||
listServers, listServersResponse);
|
||||
|
||||
ComputeService clientWhenServersExist = requestsSendResponses(requestResponseMap);
|
||||
|
||||
assertNotNull(clientWhenServersExist.listAssignableLocations());
|
||||
assertEquals(clientWhenServersExist.listAssignableLocations().size(), 1);
|
||||
|
@ -71,7 +99,7 @@ public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTe
|
|||
.method("GET")
|
||||
.endpoint(URI.create("https://compute.north.host/v1.1/3456/servers/detail"))
|
||||
.headers(
|
||||
ImmutableMultimap.<String, String> builder().put("Accept", "application/json")
|
||||
ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
|
||||
.put("X-Auth-Token", authToken).build()).build();
|
||||
|
||||
HttpResponse listServersResponse = HttpResponse.builder().statusCode(404).build();
|
||||
|
|
|
@ -23,7 +23,11 @@ import static org.testng.Assert.assertNotNull;
|
|||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.easymock.EasyMock;
|
||||
import org.jclouds.compute.domain.NodeMetadata;
|
||||
import org.jclouds.openstack.nova.v1_1.compute.domain.RegionAndName;
|
||||
import org.jclouds.openstack.nova.v1_1.domain.Address;
|
||||
import org.jclouds.openstack.nova.v1_1.domain.Server;
|
||||
import org.jclouds.openstack.nova.v1_1.domain.ServerStatus;
|
||||
|
@ -45,8 +49,11 @@ public class ServerToNodeMetadataTest {
|
|||
.privateAddresses(Address.createV4("10.0.0.1")).publicAddresses(Address.createV4("1.0.1.1"))
|
||||
.status(ServerStatus.ACTIVE).metadata(ImmutableMap.of("test", "testing")).build();
|
||||
|
||||
ServerToNodeMetadata converter = new ServerToNodeMetadata();
|
||||
LoadingCache<RegionAndName, Iterable<String>> mockLoadingCache = EasyMock.createMock(LoadingCache.class);
|
||||
EasyMock.expect(mockLoadingCache.getUnchecked(new RegionAndName(null, id.toString()))).andReturn(ImmutableSet.<String>of());
|
||||
EasyMock.replay(mockLoadingCache);
|
||||
|
||||
ServerToNodeMetadata converter = new ServerToNodeMetadata(mockLoadingCache);
|
||||
NodeMetadata convertedNodeMetadata = converter.apply(serverToConvert);
|
||||
|
||||
assertEquals(serverToConvert.getId(), convertedNodeMetadata.getId());
|
||||
|
@ -65,5 +72,7 @@ public class ServerToNodeMetadataTest {
|
|||
assertNotNull(convertedNodeMetadata.getUserMetadata());
|
||||
assertEquals(convertedNodeMetadata.getUserMetadata().size(), 1);
|
||||
assertEquals(convertedNodeMetadata.getUserMetadata().get("test"), "testing");
|
||||
}
|
||||
|
||||
EasyMock.verify(mockLoadingCache);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue