openstack-nova: wiring Floating IP functionality into ComputeServiceAdaptor and ServerToNodeMetadata

This commit is contained in:
Adam Lowe 2012-03-18 14:48:07 +00:00 committed by Adrian Cole
parent dd4d13d6bb
commit f0d6c9f10d
4 changed files with 110 additions and 20 deletions

View File

@ -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());
}

View File

@ -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();

View File

@ -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();

View File

@ -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);
}
}