diff --git a/apis/openstack-cinder/src/main/java/org/jclouds/openstack/cinder/v1/predicates/VolumePredicates.java b/apis/openstack-cinder/src/main/java/org/jclouds/openstack/cinder/v1/predicates/VolumePredicates.java index 6e26425328..26955491ab 100644 --- a/apis/openstack-cinder/src/main/java/org/jclouds/openstack/cinder/v1/predicates/VolumePredicates.java +++ b/apis/openstack-cinder/src/main/java/org/jclouds/openstack/cinder/v1/predicates/VolumePredicates.java @@ -112,16 +112,9 @@ 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()); - } + Volume volumeUpdated = volumeApi.get(volume.getId()); + checkNotNull(volumeUpdated, "Volume %s not found.", volume.getId()); + return status.equals(volumeUpdated.getStatus()); } } diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VolumeAttachmentApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VolumeAttachmentApiLiveTest.java index 4834b99b3c..02dd35b6bb 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VolumeAttachmentApiLiveTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VolumeAttachmentApiLiveTest.java @@ -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)) { diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/internal/BaseNovaApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/internal/BaseNovaApiLiveTest.java index f608e73489..614fad2f5c 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/internal/BaseNovaApiLiveTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/internal/BaseNovaApiLiveTest.java @@ -54,16 +54,15 @@ public class BaseNovaApiLiveTest extends BaseApiLiveTest { } protected Set 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 { Properties props = super.setupProperties(); setIfTestSystemPropertyPresent(props, KeystoneProperties.CREDENTIAL_TYPE); setIfTestSystemPropertyPresent(props, NovaProperties.AUTO_ALLOCATE_FLOATING_IPS); + singleRegion = setIfTestSystemPropertyPresent(props, provider + ".region"); return props; } diff --git a/core/src/test/java/org/jclouds/apis/BaseApiLiveTest.java b/core/src/test/java/org/jclouds/apis/BaseApiLiveTest.java index b951f10736..e44994c3c6 100644 --- a/core/src/test/java/org/jclouds/apis/BaseApiLiveTest.java +++ b/core/src/test/java/org/jclouds/apis/BaseApiLiveTest.java @@ -73,6 +73,17 @@ public abstract class BaseApiLiveTest { 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() diff --git a/providers/rackspace-cloudservers-us/pom.xml b/providers/rackspace-cloudservers-us/pom.xml index a8ce0a2a35..a7de79342b 100644 --- a/providers/rackspace-cloudservers-us/pom.xml +++ b/providers/rackspace-cloudservers-us/pom.xml @@ -110,6 +110,12 @@ auto-service true + + org.apache.jclouds.provider + rackspace-cloudblockstorage-us + ${project.version} + test + diff --git a/providers/rackspace-cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/us/CloudServersUSProviderMetadata.java b/providers/rackspace-cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/us/CloudServersUSProviderMetadata.java index 0ea28b757b..1224572d3f 100644 --- a/providers/rackspace-cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/us/CloudServersUSProviderMetadata.java +++ b/providers/rackspace-cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/us/CloudServersUSProviderMetadata.java @@ -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,14 +87,15 @@ 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.>builder() - .add(CloudIdentityAuthenticationApiModule.class) - .add(CloudIdentityAuthenticationModule.class) - .add(RegionModule.class) - .add(NovaParserModule.class) - .add(NovaHttpApiModule.class) - .add(CloudServersUSComputeServiceContextModule.class).build()) + .add(CloudIdentityAuthenticationApiModule.class) + .add(CloudIdentityAuthenticationModule.class) + .add(RegionModule.class) + .add(NovaParserModule.class) + .add(CloudServersUSHttpApiModule.class) + .add(CloudServersUSComputeServiceContextModule.class).build()) .build()) .homepage(URI.create("http://www.rackspace.com/cloud/nextgen")) .console(URI.create("https://mycloud.rackspace.com")) diff --git a/providers/rackspace-cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/us/config/CloudServersUSHttpApiModule.java b/providers/rackspace-cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/us/config/CloudServersUSHttpApiModule.java new file mode 100644 index 0000000000..382aced21f --- /dev/null +++ b/providers/rackspace-cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/us/config/CloudServersUSHttpApiModule.java @@ -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 { + + public CloudServersUSHttpApiModule() { + } + + @Override + protected void configure() { + bind(ImplicitOptionalConverter.class).to(PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.class); + super.configure(); + } + + @Provides + @Singleton + public Multimap aliases() { + return ImmutableMultimap.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> provideExtensionsByRegion(final Provider novaApi) { + return CacheBuilder.newBuilder().expireAfterWrite(23, TimeUnit.HOURS) + .build(new CacheLoader>() { + @Override + public Set 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); + } +} diff --git a/providers/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/compute/extensions/CloudServersUSVolumeAttachmentExtensionLiveTest.java b/providers/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/compute/extensions/CloudServersUSVolumeAttachmentExtensionLiveTest.java new file mode 100644 index 0000000000..de7b7c654b --- /dev/null +++ b/providers/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/compute/extensions/CloudServersUSVolumeAttachmentExtensionLiveTest.java @@ -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; + } + +}