Adds a rackspace-specific test and a fix for the volume-attach extension problem.

This commit is contained in:
Zack Shoylev 2015-03-19 14:37:05 -05:00
parent 3077a0cc7c
commit 8217248571
8 changed files with 228 additions and 61 deletions

View File

@ -112,18 +112,11 @@ public class VolumePredicates {
@Override
public boolean apply(Volume volume) {
checkNotNull(volume, "volume must be defined");
if (status.equals(volume.getStatus())) {
return true;
}
else {
Volume volumeUpdated = volumeApi.get(volume.getId());
checkNotNull(volumeUpdated, "Volume %s not found.", volume.getId());
return status.equals(volumeUpdated.getStatus());
}
}
}
private static class DeletedPredicate implements Predicate<Volume> {
private VolumeApi volumeApi;

View File

@ -16,8 +16,12 @@
*/
package org.jclouds.openstack.nova.v2_0.extensions;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import java.util.Properties;
import java.util.concurrent.TimeoutException;
import org.jclouds.ContextBuilder;
import org.jclouds.openstack.cinder.v1.CinderApi;
import org.jclouds.openstack.cinder.v1.domain.Volume;
@ -31,10 +35,7 @@ import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.util.concurrent.TimeoutException;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import com.google.common.collect.FluentIterable;
/**
* Tests behavior of Volume Attachment API
@ -44,10 +45,22 @@ public class VolumeAttachmentApiLiveTest extends BaseNovaApiLiveTest {
private VolumeApi volumeApi;
private VolumeAttachmentApi volumeAttachmentApi;
private String region;
private Volume volume;
private Server server;
protected String volumeProvider;
protected int volumeSizeGB;
protected String deviceId = "/dev/wtf";
@Override
protected Properties setupProperties() {
Properties props = super.setupProperties();
volumeProvider = setIfTestSystemPropertyPresent(props, provider + ".volume-provider", "openstack-cinder");
volumeSizeGB = Integer.parseInt(setIfTestSystemPropertyPresent(props, provider + ".volume-size-gb", "1"));
singleRegion = setIfTestSystemPropertyPresent(props, provider + ".region", "RegionOne");
return props;
}
@BeforeClass(groups = {"integration", "live"})
@Override
public void setup() {
@ -55,67 +68,44 @@ public class VolumeAttachmentApiLiveTest extends BaseNovaApiLiveTest {
CinderApi cinderApi;
if ("openstack-cinder".equals(getVolumeProvider())) {
cinderApi = ContextBuilder.newBuilder(getVolumeProvider())
if ("openstack-cinder".equals(volumeProvider)) {
cinderApi = ContextBuilder.newBuilder(volumeProvider)
.endpoint(endpoint)
.credentials(identity, credential)
.buildApi(CinderApi.class);
}
else {
cinderApi = ContextBuilder.newBuilder(getVolumeProvider())
cinderApi = ContextBuilder.newBuilder(volumeProvider)
.credentials(identity, credential)
.buildApi(CinderApi.class);
}
region = Iterables.getFirst(regions, "RegionOne");
volumeApi = cinderApi.getVolumeApi(region);
volumeAttachmentApi = api.getVolumeAttachmentApi(region).get();
volumeApi = cinderApi.getVolumeApi(singleRegion);
volumeAttachmentApi = api.getVolumeAttachmentApi(singleRegion).get();
CreateVolumeOptions options = CreateVolumeOptions.Builder
.name("jclouds-test-volume")
.description("description of test volume");
volume = volumeApi.create(getVolumeSizeGB(), options);
volume = volumeApi.create(volumeSizeGB, options);
VolumePredicates.awaitAvailable(volumeApi).apply(volume);
server = createServerInRegion(region);
server = createServerInRegion(singleRegion);
}
@AfterClass(groups = {"integration", "live"})
@Override
public void tearDown() {
volumeApi.delete(volume.getId());
api.getServerApi(region).delete(server.getId());
api.getServerApi(singleRegion).delete(server.getId());
super.tearDown();
}
public String getVolumeProvider() {
String volumeProviderKey = "test." + provider + ".volume-provider";
if (System.getProperties().containsKey(volumeProviderKey)) {
return System.getProperty(volumeProviderKey);
}
else {
return "openstack-cinder";
}
}
public int getVolumeSizeGB() {
String volumeSizeKey = "test." + provider + ".volume-size-gb";
if (System.getProperties().containsKey(volumeSizeKey)) {
return Integer.parseInt(System.getProperty(volumeSizeKey));
}
else {
return 1;
}
}
@Test
public void testAttachVolume() throws TimeoutException {
VolumeAttachment volumeAttachment = volumeAttachmentApi
.attachVolumeToServerAsDevice(volume.getId(), server.getId(), "/dev/wtf");
.attachVolumeToServerAsDevice(volume.getId(), server.getId(), deviceId);
// Wait for the volume to become Attached (aka In Use) before moving on
if (!VolumePredicates.awaitInUse(volumeApi).apply(volume)) {

View File

@ -54,16 +54,15 @@ public class BaseNovaApiLiveTest extends BaseApiLiveTest<NovaApi> {
}
protected Set<String> regions;
protected String singleRegion;
@BeforeClass(groups = { "integration", "live" })
@Override
public void setup() {
super.setup();
String testRegion = System.getProperty("test." + provider + ".region");
if (testRegion != null) {
regions = ImmutableSet.of(testRegion);
if (singleRegion != null) {
regions = ImmutableSet.of(singleRegion);
} else {
regions = api.getConfiguredRegions();
}
@ -82,6 +81,7 @@ public class BaseNovaApiLiveTest extends BaseApiLiveTest<NovaApi> {
Properties props = super.setupProperties();
setIfTestSystemPropertyPresent(props, KeystoneProperties.CREDENTIAL_TYPE);
setIfTestSystemPropertyPresent(props, NovaProperties.AUTO_ALLOCATE_FLOATING_IPS);
singleRegion = setIfTestSystemPropertyPresent(props, provider + ".region");
return props;
}

View File

@ -73,6 +73,17 @@ public abstract class BaseApiLiveTest<A extends Closeable> {
return null;
}
protected String setIfTestSystemPropertyPresent(Properties overrides, String key, String defaultValue) {
String val = setIfTestSystemPropertyPresent(overrides, key);
if (val == null) {
val = defaultValue;
overrides.setProperty(key, val);
}
return val;
}
/**
* This helps live testing against specific regions only.
* @param regions A list of regions, usually from getConfiguredRegions()

View File

@ -110,6 +110,12 @@
<artifactId>auto-service</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.jclouds.provider</groupId>
<artifactId>rackspace-cloudblockstorage-us</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>

View File

@ -27,7 +27,6 @@ import java.util.Properties;
import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.RegionModule;
import org.jclouds.openstack.nova.v2_0.NovaApiMetadata;
import org.jclouds.openstack.nova.v2_0.config.NovaHttpApiModule;
import org.jclouds.openstack.nova.v2_0.config.NovaParserModule;
import org.jclouds.providers.ProviderMetadata;
import org.jclouds.providers.internal.BaseProviderMetadata;
@ -35,6 +34,7 @@ import org.jclouds.rackspace.cloudidentity.v2_0.config.CloudIdentityAuthenticati
import org.jclouds.rackspace.cloudidentity.v2_0.config.CloudIdentityAuthenticationModule;
import org.jclouds.rackspace.cloudidentity.v2_0.config.CloudIdentityCredentialTypes;
import org.jclouds.rackspace.cloudservers.us.config.CloudServersUSComputeServiceContextModule;
import org.jclouds.rackspace.cloudservers.us.config.CloudServersUSHttpApiModule;
import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableSet;
@ -87,13 +87,14 @@ public class CloudServersUSProviderMetadata extends BaseProviderMetadata {
.version("2")
.defaultEndpoint("https://identity.api.rackspacecloud.com/v2.0/")
.endpointName("identity service url ending in /v2.0/")
.documentation(URI.create("http://docs.rackspace.com/loadbalancers/api/v1.0/clb-devguide/content/index.html"))
.documentation(
URI.create("http://docs.rackspace.com/loadbalancers/api/v1.0/clb-devguide/content/index.html"))
.defaultModules(ImmutableSet.<Class<? extends Module>>builder()
.add(CloudIdentityAuthenticationApiModule.class)
.add(CloudIdentityAuthenticationModule.class)
.add(RegionModule.class)
.add(NovaParserModule.class)
.add(NovaHttpApiModule.class)
.add(CloudServersUSHttpApiModule.class)
.add(CloudServersUSComputeServiceContextModule.class).build())
.build())
.homepage(URI.create("http://www.rackspace.com/cloud/nextgen"))

View File

@ -0,0 +1,124 @@
/*
* 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.rackspace.cloudservers.us.config;
import java.net.URI;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.annotation.ClientError;
import org.jclouds.http.annotation.Redirection;
import org.jclouds.http.annotation.ServerError;
import org.jclouds.openstack.nova.v2_0.NovaApi;
import org.jclouds.openstack.nova.v2_0.extensions.ExtensionNamespaces;
import org.jclouds.openstack.nova.v2_0.handlers.NovaErrorHandler;
import org.jclouds.openstack.v2_0.domain.Extension;
import org.jclouds.openstack.v2_0.functions.PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet;
import org.jclouds.rest.ConfiguresHttpApi;
import org.jclouds.rest.config.HttpApiModule;
import org.jclouds.rest.functions.ImplicitOptionalConverter;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import com.google.inject.Provides;
/**
* Configures the Rackspace connection.
*
*/
@ConfiguresHttpApi
public class CloudServersUSHttpApiModule extends HttpApiModule<NovaApi> {
public CloudServersUSHttpApiModule() {
}
@Override
protected void configure() {
bind(ImplicitOptionalConverter.class).to(PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.class);
super.configure();
}
@Provides
@Singleton
public Multimap<URI, URI> aliases() {
return ImmutableMultimap.<URI, URI>builder()
.put(URI.create(ExtensionNamespaces.SECURITY_GROUPS),
URI.create("http://docs.openstack.org/compute/ext/securitygroups/api/v1.1"))
.put(URI.create(ExtensionNamespaces.FLOATING_IPS),
URI.create("http://docs.openstack.org/compute/ext/floating_ips/api/v1.1"))
.put(URI.create(ExtensionNamespaces.KEYPAIRS),
URI.create("http://docs.openstack.org/compute/ext/keypairs/api/v1.1"))
.put(URI.create(ExtensionNamespaces.SIMPLE_TENANT_USAGE),
URI.create("http://docs.openstack.org/compute/ext/os-simple-tenant-usage/api/v1.1"))
.put(URI.create(ExtensionNamespaces.HOSTS),
URI.create("http://docs.openstack.org/compute/ext/hosts/api/v1.1"))
.put(URI.create(ExtensionNamespaces.VOLUMES),
URI.create("http://docs.openstack.org/compute/ext/volumes/api/v1.1"))
.put(URI.create(ExtensionNamespaces.VIRTUAL_INTERFACES),
URI.create("http://docs.openstack.org/compute/ext/virtual_interfaces/api/v1.1"))
.put(URI.create(ExtensionNamespaces.CREATESERVEREXT),
URI.create("http://docs.openstack.org/compute/ext/createserverext/api/v1.1"))
.put(URI.create(ExtensionNamespaces.ADMIN_ACTIONS),
URI.create("http://docs.openstack.org/compute/ext/admin-actions/api/v1.1"))
.put(URI.create(ExtensionNamespaces.AGGREGATES),
URI.create("http://docs.openstack.org/compute/ext/aggregates/api/v1.1"))
.put(URI.create(ExtensionNamespaces.FLAVOR_EXTRA_SPECS),
URI.create("http://docs.openstack.org/compute/ext/flavor_extra_specs/api/v1.1"))
.put(URI.create(ExtensionNamespaces.QUOTAS),
URI.create("http://docs.openstack.org/compute/ext/quotas-sets/api/v1.1"))
.put(URI.create(ExtensionNamespaces.VOLUME_TYPES),
URI.create("http://docs.openstack.org/compute/ext/volume_types/api/v1.1"))
.put(URI.create(ExtensionNamespaces.AVAILABILITY_ZONE),
URI.create("http://docs.openstack.org/compute/ext/availabilityzone/api/v1.1"))
/**
* Only change - this is to ensure that for rackspace, the extension detection for
* VOLUME_ATTACHMENTS is based on discovering this namespace (VOLUMES).
*/
.put(URI.create(ExtensionNamespaces.VOLUME_ATTACHMENTS),
//URI.create("http://docs.openstack.org/compute/ext/os-volume-attachment-update/api/v2"))
URI.create("http://docs.openstack.org/compute/ext/volumes/api/v1.1"))
.put(URI.create(ExtensionNamespaces.ATTACH_INTERFACES),
URI.create("http://docs.openstack.org/compute/ext/interfaces/api/v1.1"))
.build();
}
@Provides
@Singleton
public LoadingCache<String, Set<? extends Extension>> provideExtensionsByRegion(final Provider<NovaApi> novaApi) {
return CacheBuilder.newBuilder().expireAfterWrite(23, TimeUnit.HOURS)
.build(new CacheLoader<String, Set<? extends Extension>>() {
@Override
public Set<? extends Extension> load(String key) throws Exception {
return novaApi.get().getExtensionApi(key).list();
}
});
}
@Override
protected void bindErrorHandlers() {
bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(NovaErrorHandler.class);
bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(NovaErrorHandler.class);
bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(NovaErrorHandler.class);
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.rackspace.cloudservers.us.compute.extensions;
import java.util.Properties;
import org.jclouds.openstack.nova.v2_0.extensions.VolumeAttachmentApiLiveTest;
import org.testng.annotations.Test;
@Test(groups = "live", singleThreaded = true, testName = "CloudServersUSVolumeAttachmentExtensionLivetest")
public class CloudServersUSVolumeAttachmentExtensionLiveTest extends VolumeAttachmentApiLiveTest {
public CloudServersUSVolumeAttachmentExtensionLiveTest() {
provider = "rackspace-cloudservers-us";
// Specifying a device currently does not work for rackspace and causes issues
deviceId = "";
}
@Override
protected Properties setupProperties() {
Properties props = super.setupProperties();
volumeProvider = "rackspace-cloudblockstorage-us";
volumeSizeGB = 80;
singleRegion = "IAD";
return props;
}
}