Merge pull request #413 from jdaggett/develop

openstack-nova changes
This commit is contained in:
Adrian Cole 2012-03-09 11:40:39 -08:00
commit 6df17b858d
6 changed files with 475 additions and 25 deletions

View File

@ -23,12 +23,7 @@ import java.util.Set;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.location.Region;
import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
import org.jclouds.openstack.nova.v1_1.features.FlavorClient;
import org.jclouds.openstack.nova.v1_1.features.FloatingIPClient;
import org.jclouds.openstack.nova.v1_1.features.ImageClient;
import org.jclouds.openstack.nova.v1_1.features.KeyPairClient;
import org.jclouds.openstack.nova.v1_1.features.SecurityGroupClient;
import org.jclouds.openstack.nova.v1_1.features.ServerAsyncClient;
import org.jclouds.openstack.nova.v1_1.features.*;
import org.jclouds.rest.annotations.Delegate;
import org.jclouds.rest.annotations.EndpointParam;
@ -63,35 +58,35 @@ public interface NovaAsyncClient {
* Provides asynchronous access to Flavor features.
*/
@Delegate
FlavorClient getFlavorClientForRegion(
FlavorAsyncClient getFlavorClientForRegion(
@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
/**
* Provides asynchronous access to Image features.
*/
@Delegate
ImageClient getImageClientForRegion(
ImageAsyncClient getImageClientForRegion(
@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
/**
* Provides asynchronous access to Floating IP features.
*/
@Delegate
FloatingIPClient getFloatingIPClientForRegion(
FloatingIPAsyncClient getFloatingIPClientForRegion(
@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
/**
* Provides asynchronous access to Security Group features.
*/
@Delegate
SecurityGroupClient getSecurityGroupClientForRegion(
SecurityGroupAsyncClient getSecurityGroupClientForRegion(
@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
/**
* Provides asynchronous access to Key Pair features.
*/
@Delegate
KeyPairClient getKeyPairClientForRegion(
KeyPairAsyncClient getKeyPairClientForRegion(
@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
}

View File

@ -21,21 +21,20 @@ package org.jclouds.openstack.nova.v1_1.domain;
import static com.google.common.base.Objects.toStringHelper;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.*;
import org.jclouds.openstack.domain.Link;
import org.jclouds.openstack.domain.Resource;
import org.jclouds.openstack.nova.v1_1.domain.Address.Type;
import org.jclouds.util.InetAddresses2;
import org.jclouds.util.Multimaps2;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.gson.annotations.SerializedName;
/**
@ -354,23 +353,55 @@ public class Server extends Resource {
* @return the private ip addresses assigned to the server
*/
public Set<Address> getPrivateAddresses() {
return ImmutableSet.copyOf(addresses.get(Address.Type.PRIVATE));
Collection<Address> privateAddresses = getAddresses().get(Address.Type.PRIVATE);
if (privateAddresses == null) {
return ImmutableSet.<Address> of();
} else {
return ImmutableSet.copyOf(privateAddresses);
}
}
/**
* @return the public ip addresses assigned to the server
*/
public Set<Address> getPublicAddresses() {
return ImmutableSet.copyOf(addresses.get(Address.Type.PUBLIC));
Collection<Address> publicAddrs = getAddresses().get(Address.Type.PUBLIC);
if (publicAddrs == null) {
return ImmutableSet.<Address> of();
} else {
return ImmutableSet.copyOf(publicAddrs);
}
}
/**
* @return the ip addresses assigned to the server
*/
public Multimap<Type, Address> getAddresses() {
return Multimaps2.fromOldSchool(addresses);
ImmutableSetMultimap.Builder returnMapBuilder = new ImmutableSetMultimap.Builder<Type, Address>();
Set<Address> publicAddresses = addresses.get(Address.Type.PUBLIC);
Set<Address> privateAddresses = addresses.get(Address.Type.PRIVATE);
if (privateAddresses.size() > 1) {
if (publicAddresses != null) {
returnMapBuilder.putAll(Address.Type.PUBLIC, Iterables.filter(publicAddresses, Predicates.not(IsPrivateAddress.INSTANCE)));
}
returnMapBuilder.putAll(Address.Type.PRIVATE, Iterables.filter(privateAddresses, IsPrivateAddress.INSTANCE));
returnMapBuilder.putAll(Address.Type.PUBLIC, Iterables.filter(privateAddresses, Predicates.not(IsPrivateAddress.INSTANCE)));
} else {
return Multimaps2.fromOldSchool(addresses);
}
return returnMapBuilder.build();
}
private static enum IsPrivateAddress implements Predicate<Address> {
INSTANCE;
public boolean apply (Address in) {
return InetAddresses2.IsPrivateIPAddress.INSTANCE.apply(in.getAddr());
}
}
@Override
public String toString() {
return toStringHelper("").add("id", id).add("name", name)

View File

@ -0,0 +1,166 @@
package org.jclouds.openstack.nova.v1_1.features;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.openstack.nova.v1_1.NovaClient;
import org.jclouds.openstack.nova.v1_1.internal.BaseNovaRestClientExpectTest;
import org.jclouds.openstack.nova.v1_1.parse.ParseFloatingIPListTest;
import org.jclouds.openstack.nova.v1_1.parse.ParseFloatingIPTest;
import org.testng.annotations.Test;
import java.net.URI;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
/**
* Tests annotation parsing of {@code FloatingIPAsyncClient}
*
* @author Michael Arnold
*/
@Test(groups = "unit", testName = "FloatingIPClientExpectTest")
public class FloatingIPClientExpectTest extends BaseNovaRestClientExpectTest {
public void testListFloatingIPsWhenResponseIs2xx() throws Exception {
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();
HttpResponse listFloatingIPsResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource("/floatingip_list.json")).build();
NovaClient clientWhenFloatingIPsExist = requestsSendResponses(
keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess,
listFloatingIPs, listFloatingIPsResponse);
assertEquals(clientWhenFloatingIPsExist.getConfiguredRegions(),
ImmutableSet.of("North"));
assertEquals(clientWhenFloatingIPsExist.getFloatingIPClientForRegion("North")
.listFloatingIPs().toString(), new ParseFloatingIPListTest().expected()
.toString());
}
public void testListFloatingIPsWhenResponseIs404() throws Exception {
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();
HttpResponse listFloatingIPsResponse = HttpResponse.builder().statusCode(404)
.build();
NovaClient clientWhenNoServersExist = requestsSendResponses(
keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess,
listFloatingIPs, listFloatingIPsResponse);
assertTrue(clientWhenNoServersExist.getFloatingIPClientForRegion("North")
.listFloatingIPs().isEmpty());
}
public void testGetFloatingIPWhenResponseIs2xx() throws Exception {
HttpRequest getFloatingIP = HttpRequest
.builder()
.method("GET")
.endpoint(
URI.create("https://compute.north.host/v1.1/3456/os-floating-ips/1"))
.headers(
ImmutableMultimap.<String, String> builder()
.put("Accept", "application/json")
.put("X-Auth-Token", authToken).build()).build();
HttpResponse getFloatingIPResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource("/floatingip_details.json")).build();
NovaClient clientWhenFloatingIPsExist = requestsSendResponses(
keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess,
getFloatingIP, getFloatingIPResponse);
assertEquals(clientWhenFloatingIPsExist.getFloatingIPClientForRegion("North")
.getFloatingIP("1").toString(),
new ParseFloatingIPTest().expected().toString());
}
public void testGetFloatingIPWhenResponseIs404() throws Exception {
HttpRequest getFloatingIP = HttpRequest
.builder()
.method("GET")
.endpoint(
URI.create("https://compute.north.host/v1.1/3456/os-floating-ips/1"))
.headers(
ImmutableMultimap.<String, String> builder()
.put("Accept", "application/json")
.put("X-Auth-Token", authToken).build()).build();
HttpResponse getFloatingIPResponse = HttpResponse.builder().statusCode(404).build();
NovaClient clientWhenNoServersExist = requestsSendResponses(
keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess,
getFloatingIP, getFloatingIPResponse);
assertNull(clientWhenNoServersExist.getFloatingIPClientForRegion("North")
.getFloatingIP("1"));
}
public void testAllocateWhenResponseIs2xx() throws Exception {
HttpRequest allocateFloatingIP = HttpRequest
.builder()
.method("POST")
.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())
.payload(payloadFromStringWithContentType("{}", "application/json")).build();
HttpResponse allocateFloatingIPResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource("/floatingip_details.json")).build();
NovaClient clientWhenFloatingIPsExist = requestsSendResponses(
keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess,
allocateFloatingIP, allocateFloatingIPResponse);
assertEquals(clientWhenFloatingIPsExist.getFloatingIPClientForRegion("North")
.allocate().toString(),
new ParseFloatingIPTest().expected().toString());
}
public void testAllocateWhenResponseIs404() throws Exception {
HttpRequest allocateFloatingIP = HttpRequest
.builder()
.method("POST")
.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())
.payload(payloadFromStringWithContentType("{}", "application/json")).build();
HttpResponse allocateFloatingIPResponse = HttpResponse.builder().statusCode(404).build();
NovaClient clientWhenNoServersExist = requestsSendResponses(
keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess,
allocateFloatingIP, allocateFloatingIPResponse);
assertNull(clientWhenNoServersExist.getFloatingIPClientForRegion("North")
.allocate());
}
}

View File

@ -18,15 +18,19 @@
*/
package org.jclouds.openstack.nova.v1_1.features;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.util.Set;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.ListenableFuture;
import org.jclouds.openstack.nova.v1_1.domain.Address;
import org.jclouds.openstack.nova.v1_1.domain.FloatingIP;
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.internal.BaseNovaClientLiveTest;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test;
import java.util.Set;
import static org.testng.Assert.*;
/**
* Tests behavior of {@code ServerClient}
@ -36,6 +40,8 @@ import org.testng.annotations.Test;
@Test(groups = "live", testName = "FloatingIPClientLiveTest")
public class FloatingIPClientLiveTest extends BaseNovaClientLiveTest {
private static final int INCONSISTENCY_WINDOW = 5000;
@Test
public void testListFloatingIPs() throws Exception {
for (String regionId : context.getApi().getConfiguredRegions()) {
@ -56,4 +62,114 @@ public class FloatingIPClientLiveTest extends BaseNovaClientLiveTest {
}
}
}
@Test
public void testAllocateAndDeallocateFloatingIPs() throws Exception {
for (String regionId : context.getApi().getConfiguredRegions()) {
FloatingIPClient client = context.getApi().getFloatingIPClientForRegion(regionId);
FloatingIP floatingIP = client.allocate();
assertNotNull(floatingIP);
Set<FloatingIP> response = client.listFloatingIPs();
boolean ipInSet = false;
for (FloatingIP ip : response) {
if (ip.getId().equals(floatingIP.getId())) ipInSet = true;
}
assertTrue(ipInSet);
client.deallocate(floatingIP.getId());
response = client.listFloatingIPs();
ipInSet = false;
for (FloatingIP ip : response) {
if (ip.getId().equals(floatingIP.getId())) {
ipInSet = true;
}
}
assertFalse(ipInSet);
}
}
@Test
public void testAddAndRemoveFloatingIp() throws Exception {
for (String regionId : context.getApi().getConfiguredRegions()) {
FloatingIPClient client = context.getApi().getFloatingIPClientForRegion(regionId);
ServerClient serverClient = context.getApi().getServerClientForRegion(regionId);
Server server = serverClient.createServer("test", "121", "100");
blockUntilServerActive(server.getId(), serverClient);
FloatingIP floatingIP = client.allocate();
assertNotNull(floatingIP);
try {
client.addFloatingIP(server.getId(), floatingIP.getIp());
assertEventually(new ServerHasFloatingIP(serverClient, server.getId(), floatingIP.getIp()));
}
finally {
client.removeFloatingIP(server.getId(), floatingIP.getIp());
serverClient.deleteServer(server.getId());
}
}
}
private void blockUntilServerActive(String serverId, ServerClient client) throws InterruptedException {
Server currentDetails = null;
for (currentDetails = client.getServer(serverId); currentDetails.getStatus() != ServerStatus.ACTIVE; currentDetails = client
.getServer(serverId)) {
System.out.printf("blocking on status active%n%s%n", currentDetails);
Thread.sleep(5 * 1000);
}
}
protected static void assertEventually(Runnable assertion) {
long start = System.currentTimeMillis();
AssertionError error = null;
for (int i = 0; i < 30; i++) {
try {
assertion.run();
if (i > 0)
System.err.printf("%d attempts and %dms asserting %s%n", i + 1, System.currentTimeMillis() - start,
assertion.getClass().getSimpleName());
return;
} catch (AssertionError e) {
error = e;
}
try {
Thread.sleep(INCONSISTENCY_WINDOW / 30);
} catch (InterruptedException e) {
}
}
if (error != null)
throw error;
}
public static final class ServerHasFloatingIP implements Runnable {
private final ServerClient client;
private final String serverId;
private final String floatingIP;
public ServerHasFloatingIP(ServerClient serverClient, String serverId, String floatingIP) {
this.client = serverClient;
this.serverId = serverId;
this.floatingIP = floatingIP;
}
public void run() {
try {
Server server = client.getServer(serverId);
boolean ipInServerAddresses = false;
Multimap<Address.Type, Address>addresses = server.getAddresses();
for (Address address : addresses.values()){
if (address.getAddr().equals(floatingIP)) {
ipInServerAddresses = true;
}
}
assertTrue(ipInServerAddresses);
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
}

View File

@ -0,0 +1,77 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.nova.v1_1.parse;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.jclouds.json.BaseSetParserTest;
import org.jclouds.json.config.GsonModule;
import org.jclouds.openstack.domain.Link;
import org.jclouds.openstack.domain.Link.Relation;
import org.jclouds.openstack.domain.Resource;
import org.jclouds.openstack.nova.v1_1.config.NovaParserModule;
import org.jclouds.openstack.nova.v1_1.domain.FloatingIP;
import org.jclouds.rest.annotations.SelectJson;
import org.testng.annotations.Test;
import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import java.net.URI;
import java.util.Set;
/**
*
* @author Michael Arnold
*/
@Test(groups = "unit", testName = "ParseFloatingIPListTest")
public class ParseFloatingIPListTest extends BaseSetParserTest<FloatingIP> {
@Override
public String resource() {
return "/floatingip_list.json";
}
@Override
@SelectJson("floating_ips")
@Consumes(MediaType.APPLICATION_JSON)
public Set<FloatingIP> expected() {
return ImmutableSet
.of(
FloatingIP
.builder()
.id("1")
.instanceId("12")
.ip("10.0.0.3")
.fixedIp("11.0.0.1")
.build(),
FloatingIP
.builder()
.id("2")
.instanceId(null)
.ip("10.0.0.5")
.fixedIp(null)
.build());
}
protected Injector injector() {
return Guice.createInjector(new NovaParserModule(), new GsonModule());
}
}

View File

@ -0,0 +1,65 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.nova.v1_1.parse;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.jclouds.json.BaseItemParserTest;
import org.jclouds.json.config.GsonModule;
import org.jclouds.openstack.domain.Link;
import org.jclouds.openstack.domain.Link.Relation;
import org.jclouds.openstack.nova.v1_1.config.NovaParserModule;
import org.jclouds.openstack.nova.v1_1.domain.Flavor;
import org.jclouds.openstack.nova.v1_1.domain.FloatingIP;
import org.jclouds.rest.annotations.SelectJson;
import org.testng.annotations.Test;
import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import java.net.URI;
/**
* @author Jeremy Daggett
*/
@Test(groups = "unit", testName = "ParseFloatingIPTest")
public class ParseFloatingIPTest extends BaseItemParserTest<FloatingIP> {
@Override
public String resource() {
return "/floatingip_details.json";
}
@Override
@SelectJson("floating_ip")
@Consumes(MediaType.APPLICATION_JSON)
public FloatingIP expected() {
return FloatingIP
.builder()
.id("1")
.instanceId("123")
.fixedIp("10.0.0.2")
.ip("10.0.0.3")
.build();
}
protected Injector injector() {
return Guice.createInjector(new NovaParserModule(), new GsonModule());
}
}