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.annotation.Nullable;
import javax.inject.Inject; 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.ComputeServiceAdapter;
import org.jclouds.compute.domain.Template; import org.jclouds.compute.domain.Template;
import org.jclouds.domain.Location; import org.jclouds.domain.Location;
import org.jclouds.domain.LocationBuilder; import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LoginCredentials; import org.jclouds.domain.LoginCredentials;
import org.jclouds.openstack.nova.v1_1.NovaClient; import org.jclouds.openstack.nova.v1_1.NovaClient;
import org.jclouds.openstack.nova.v1_1.domain.Flavor; import org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions;
import org.jclouds.openstack.nova.v1_1.domain.Image; import org.jclouds.openstack.nova.v1_1.domain.*;
import org.jclouds.openstack.nova.v1_1.domain.RebootType; import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPClient;
import org.jclouds.openstack.nova.v1_1.domain.Server;
import org.jclouds.openstack.nova.v1_1.features.FlavorClient; 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.ImageClient;
import org.jclouds.openstack.nova.v1_1.features.ServerClient; 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.base.Function;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; 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 * The adapter used by the NovaComputeServiceContextModule to interface the
* nova-specific domain model to the computeService generic domain model. * nova-specific domain model to the computeService generic domain model.
* *
* @author Matt Stephenson * @author Matt Stephenson
*/ */
public class NovaComputeServiceAdapter implements ComputeServiceAdapter<Server, Flavor, Image, Location> { 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 FlavorClient defaultFlavorClient;
private final ImageClient defaultImageClient; private final ImageClient defaultImageClient;
private final Set<String> regions; private final Set<String> regions;
private final String defaultRegion;
@Inject
@Named(NovaConstants.PROPERTY_NOVA_AUTO_ALLOCATE_FLOATING_IPS)
@VisibleForTesting
boolean autoAllocateFloatingIps = false;
@Inject @Inject
public NovaComputeServiceAdapter(NovaClient novaClient) { public NovaComputeServiceAdapter(NovaClient novaClient) {
@ -63,20 +74,47 @@ public class NovaComputeServiceAdapter implements ComputeServiceAdapter<Server,
throw new IllegalStateException( throw new IllegalStateException(
"No regions exist for this compute service. The Nova compute service requires at least 1 region."); "No regions exist for this compute service. The Nova compute service requires at least 1 region.");
} }
String region = regions.iterator().next(); this.defaultRegion = regions.iterator().next();
this.defaultLocationServerClient = novaClient.getServerClientForRegion(region); this.defaultLocationServerClient = novaClient.getServerClientForRegion(defaultRegion);
this.defaultFlavorClient = novaClient.getFlavorClientForRegion(region); this.defaultFlavorClient = novaClient.getFlavorClientForRegion(defaultRegion);
this.defaultImageClient = novaClient.getImageClientForRegion(region); this.defaultImageClient = novaClient.getImageClientForRegion(defaultRegion);
} }
@Override @Override
public NodeAndInitialCredentials<Server> createNodeWithGroupEncodedIntoName(String tag, String name, public NodeAndInitialCredentials<Server> createNodeWithGroupEncodedIntoName(String tag, String name,
Template template) { Template template) {
ServerClient serverClient = template.getLocation() != null ? novaClient.getServerClientForRegion(template String region = template.getLocation() == null ? defaultRegion : template.getLocation().getId();
.getLocation().getId()) : defaultLocationServerClient; ServerClient serverClient = template.getLocation() != null ? novaClient.getServerClientForRegion(template.getLocation().getId()) : defaultLocationServerClient;
// TODO: make NovaTemplateOptions with the following: // TODO: make NovaTemplateOptions with the following:
// security group, key pair, floating ip (attach post server-create?) // 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()); 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() return new NodeAndInitialCredentials<Server>(server, server.getId() + "", LoginCredentials.builder()
.password(server.getAdminPass()).build()); .password(server.getAdminPass()).build());
} }

View File

@ -18,8 +18,10 @@
*/ */
package org.jclouds.openstack.nova.v1_1.compute.functions; 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.NodeMetadata;
import org.jclouds.compute.domain.NodeMetadataBuilder; 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.Address;
import org.jclouds.openstack.nova.v1_1.domain.Server; 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.ImmutableMap;
import com.google.common.collect.Iterables; 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 * A function for transforming a nova-specific Server into a generic
* NodeMetadata object. * NodeMetadata object.
* *
* @author Matt Stephenson * @author Matt Stephenson
*/ */
public class ServerToNodeMetadata implements Function<Server, NodeMetadata> { 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 @Override
public NodeMetadata apply(Server server) { public NodeMetadata apply(Server server) {
return new NodeMetadataBuilder() return new NodeMetadataBuilder()
// TODO: scope id to region, if there's a chance for conflict // TODO: scope id to region, if there's a chance for conflict
// TODO: pass correct region into floatingIpCache request
.id(server.getId()) .id(server.getId())
.providerId(server.getId()) .providerId(server.getId())
.name(server.getName()) .name(server.getName())
.publicAddresses( .publicAddresses(
Iterables.transform(server.getPublicAddresses(), new AddressToStringTransformationFunction())) Iterables.concat(Iterables.transform(server.getPublicAddresses(), new AddressToStringTransformationFunction()),
floatingIpCache.getUnchecked(new RegionAndName(null, server.getId()))))
.privateAddresses( .privateAddresses(
Iterables.transform(server.getPrivateAddresses(), new AddressToStringTransformationFunction())) Iterables.transform(server.getPrivateAddresses(), new AddressToStringTransformationFunction()))
.state(server.getStatus().getNodeState()).userMetadata(ImmutableMap.copyOf(server.getMetadata())).build(); .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 static org.testng.Assert.assertTrue;
import java.net.URI; import java.net.URI;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import org.jclouds.compute.ComputeService; import org.jclouds.compute.ComputeService;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
@ -34,26 +36,52 @@ import com.google.common.collect.ImmutableMultimap;
/** /**
* Tests the compute service abstraction of the nova client. * Tests the compute service abstraction of the nova client.
* *
* @author Matt Stephenson * @author Matt Stephenson
*/ */
@Test(groups = "unit", testName = "NovaComputeServiceExpectTest") @Test(groups = "unit", testName = "NovaComputeServiceExpectTest")
public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTest { public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTest {
public void testListServersWhenResponseIs2xx() throws Exception { 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 HttpRequest listServers = HttpRequest
.builder() .builder()
.method("GET") .method("GET")
.endpoint(URI.create("https://compute.north.host/v1.1/3456/servers/detail")) .endpoint(URI.create("https://compute.north.host/v1.1/3456/servers/detail"))
.headers( .headers(
ImmutableMultimap.<String, String> builder().put("Accept", "application/json") ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
.put("X-Auth-Token", authToken).build()).build(); .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) HttpResponse listServersResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource("/server_list_details.json")).build(); .payload(payloadFromResource("/server_list_details.json")).build();
ComputeService clientWhenServersExist = requestsSendResponses(keystoneAuthWithAccessKeyAndSecretKey, Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.of(
responseWithKeystoneAccess, listServers, listServersResponse); keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess,
listFloatingIps, listFloatingIpsResponse,
getExtensions, getExtensionsResponse,
listServers, listServersResponse);
ComputeService clientWhenServersExist = requestsSendResponses(requestResponseMap);
assertNotNull(clientWhenServersExist.listAssignableLocations()); assertNotNull(clientWhenServersExist.listAssignableLocations());
assertEquals(clientWhenServersExist.listAssignableLocations().size(), 1); assertEquals(clientWhenServersExist.listAssignableLocations().size(), 1);
@ -71,7 +99,7 @@ public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTe
.method("GET") .method("GET")
.endpoint(URI.create("https://compute.north.host/v1.1/3456/servers/detail")) .endpoint(URI.create("https://compute.north.host/v1.1/3456/servers/detail"))
.headers( .headers(
ImmutableMultimap.<String, String> builder().put("Accept", "application/json") ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
.put("X-Auth-Token", authToken).build()).build(); .put("X-Auth-Token", authToken).build()).build();
HttpResponse listServersResponse = HttpResponse.builder().statusCode(404).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 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.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.Address;
import org.jclouds.openstack.nova.v1_1.domain.Server; import org.jclouds.openstack.nova.v1_1.domain.Server;
import org.jclouds.openstack.nova.v1_1.domain.ServerStatus; 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")) .privateAddresses(Address.createV4("10.0.0.1")).publicAddresses(Address.createV4("1.0.1.1"))
.status(ServerStatus.ACTIVE).metadata(ImmutableMap.of("test", "testing")).build(); .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); NodeMetadata convertedNodeMetadata = converter.apply(serverToConvert);
assertEquals(serverToConvert.getId(), convertedNodeMetadata.getId()); assertEquals(serverToConvert.getId(), convertedNodeMetadata.getId());
@ -65,5 +72,7 @@ public class ServerToNodeMetadataTest {
assertNotNull(convertedNodeMetadata.getUserMetadata()); assertNotNull(convertedNodeMetadata.getUserMetadata());
assertEquals(convertedNodeMetadata.getUserMetadata().size(), 1); assertEquals(convertedNodeMetadata.getUserMetadata().size(), 1);
assertEquals(convertedNodeMetadata.getUserMetadata().get("test"), "testing"); assertEquals(convertedNodeMetadata.getUserMetadata().get("test"), "testing");
}
EasyMock.verify(mockLoadingCache);
}
} }