Added support for the OpenStack Folsom Cinder Block Storage API.

This commit is contained in:
Everett Toews 2012-10-31 16:07:55 -05:00
parent 851e7bf6ec
commit 3292701883
41 changed files with 3612 additions and 1 deletions

View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-project</artifactId>
<version>1.6.0-SNAPSHOT</version>
<relativePath>../../project/pom.xml</relativePath>
</parent>
<groupId>org.jclouds.labs</groupId>
<artifactId>openstack-cinder</artifactId>
<name>jclouds openstack-cinder api</name>
<description>jclouds components to access an implementation of OpenStack Cinder</description>
<packaging>bundle</packaging>
<properties>
<!-- keystone endpoint -->
<test.openstack-cinder.endpoint>http://localhost:5000/v2.0/</test.openstack-cinder.endpoint>
<!-- keystone version -->
<test.openstack-cinder.api-version>1</test.openstack-cinder.api-version>
<test.openstack-cinder.build-version></test.openstack-cinder.build-version>
<test.openstack-cinder.identity>FIXME_IDENTITY</test.openstack-cinder.identity>
<test.openstack-cinder.credential>FIXME_CREDENTIALS</test.openstack-cinder.credential>
<test.jclouds.keystone.credential-type>passwordCredentials</test.jclouds.keystone.credential-type>
<jclouds.osgi.export>org.jclouds.openstack.cinder.v1*;version="${project.version}"</jclouds.osgi.export>
<jclouds.osgi.import>
org.jclouds.rest.internal;version="${project.version}",
org.jclouds*;version="${project.version}",
*
</jclouds.osgi.import>
</properties>
<dependencies>
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jclouds.api</groupId>
<artifactId>openstack-keystone</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-core</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jclouds.api</groupId>
<artifactId>openstack-keystone</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>live</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>integration</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<systemPropertyVariables>
<test.openstack-cinder.endpoint>${test.openstack-cinder.endpoint}</test.openstack-cinder.endpoint>
<test.openstack-cinder.api-version>${test.openstack-cinder.api-version}</test.openstack-cinder.api-version>
<test.openstack-cinder.build-version>${test.openstack-cinder.build-version}</test.openstack-cinder.build-version>
<test.openstack-cinder.identity>${test.openstack-cinder.identity}</test.openstack-cinder.identity>
<test.openstack-cinder.credential>${test.openstack-cinder.credential}</test.openstack-cinder.credential>
<test.jclouds.keystone.credential-type>${test.jclouds.keystone.credential-type}</test.jclouds.keystone.credential-type>
<jclouds.blobstore.httpstream.url>${jclouds.blobstore.httpstream.url}</jclouds.blobstore.httpstream.url>
<jclouds.blobstore.httpstream.md5>${jclouds.blobstore.httpstream.md5}</jclouds.blobstore.httpstream.md5>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,80 @@
/**
* 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.cinder.v1;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.jclouds.concurrent.Timeout;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.location.Zone;
import org.jclouds.location.functions.ZoneToEndpoint;
import org.jclouds.openstack.cinder.v1.features.SnapshotApi;
import org.jclouds.openstack.cinder.v1.features.VolumeApi;
import org.jclouds.openstack.cinder.v1.features.VolumeTypeApi;
import org.jclouds.openstack.v2_0.features.ExtensionApi;
import org.jclouds.rest.annotations.Delegate;
import org.jclouds.rest.annotations.EndpointParam;
import com.google.inject.Provides;
/**
* Provides synchronous access to Cinder.
*
* @see CinderAsyncApi
* @see <a href="http://api.openstack.org/">API Doc</a>
* @author Everett Toews
*/
@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS)
public interface CinderApi {
/**
* @return the Zone codes configured
*/
@Provides
@Zone
Set<String> getConfiguredZones();
/**
* Provides synchronous access to Extension features.
*/
@Delegate
ExtensionApi getExtensionApiForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
/**
* Provides synchronous access to Volume features.
*/
@Delegate
VolumeApi getVolumeApiForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
/**
* Provides synchronous access to VolumeType features.
*/
@Delegate
VolumeTypeApi getVolumeTypeApiForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
/**
* Provides synchronous access to Snapshot features.
*/
@Delegate
SnapshotApi getSnapshotApiForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
}

View File

@ -0,0 +1,108 @@
/**
* 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.cinder.v1;
import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.SERVICE_TYPE;
import java.net.URI;
import java.util.Properties;
import org.jclouds.apis.ApiMetadata;
import org.jclouds.openstack.cinder.v1.config.CinderParserModule;
import org.jclouds.openstack.cinder.v1.config.CinderRestClientModule;
import org.jclouds.openstack.keystone.v2_0.config.CredentialTypes;
import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule;
import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.ZoneModule;
import org.jclouds.openstack.v2_0.ServiceType;
import org.jclouds.rest.RestContext;
import org.jclouds.rest.internal.BaseRestApiMetadata;
import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.TypeToken;
import com.google.inject.Module;
/**
* Implementation of {@link ApiMetadata} for Cinder v1 API
*
* @author Everett Toews
*/
public class CinderApiMetadata extends BaseRestApiMetadata {
private static final long serialVersionUID = 4525672099385581765L;
public static final TypeToken<RestContext<CinderApi, CinderAsyncApi>> CONTEXT_TOKEN = new TypeToken<RestContext<CinderApi, CinderAsyncApi>>() {
private static final long serialVersionUID = -2850937833892503251L;
};
@Override
public Builder toBuilder() {
return new Builder().fromApiMetadata(this);
}
public CinderApiMetadata() {
this(new Builder());
}
protected CinderApiMetadata(Builder builder) {
super(builder);
}
public static Properties defaultProperties() {
Properties properties = BaseRestApiMetadata.defaultProperties();
properties.setProperty(SERVICE_TYPE, ServiceType.BLOCK_STORAGE);
properties.setProperty(CREDENTIAL_TYPE, CredentialTypes.PASSWORD_CREDENTIALS);
return properties;
}
public static class Builder extends BaseRestApiMetadata.Builder {
protected Builder() {
super(CinderApi.class, CinderAsyncApi.class);
id("openstack-cinder")
.name("OpenStack Cinder Folsom API")
.identityName("${tenantName}:${userName} or ${userName}, if your keystone supports a default tenant")
.credentialName("${password}")
.endpointName("Keystone base URL ending in /v2.0/")
.documentation(URI.create("http://api.openstack.org/"))
.version("1")
.defaultEndpoint("http://localhost:5000/v2.0/")
.defaultProperties(CinderApiMetadata.defaultProperties())
.defaultModules(ImmutableSet.<Class<? extends Module>>builder()
.add(KeystoneAuthenticationModule.class)
.add(ZoneModule.class)
.add(CinderParserModule.class)
.add(CinderRestClientModule.class)
.build());
}
@Override
public CinderApiMetadata build() {
return new CinderApiMetadata(this);
}
@Override
public Builder fromApiMetadata(ApiMetadata in) {
super.fromApiMetadata(in);
return this;
}
}
}

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.cinder.v1;
import java.util.Set;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.location.Zone;
import org.jclouds.location.functions.ZoneToEndpoint;
import org.jclouds.openstack.cinder.v1.features.SnapshotAsyncApi;
import org.jclouds.openstack.cinder.v1.features.VolumeAsyncApi;
import org.jclouds.openstack.cinder.v1.features.VolumeTypeAsyncApi;
import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi;
import org.jclouds.rest.annotations.Delegate;
import org.jclouds.rest.annotations.EndpointParam;
import com.google.inject.Provides;
/**
* Provides asynchronous access to Cinder via its REST API.
*
* @see CinderApi
* @see <a href="http://api.openstack.org/">API Doc</a>
* @author Everett Toews
*/
public interface CinderAsyncApi {
/**
* @return the Zone codes configured
*/
@Provides
@Zone
Set<String> getConfiguredZones();
/**
* Provides asynchronous access to Extension features.
*/
@Delegate
ExtensionAsyncApi getExtensionApiForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
/**
* Provides asynchronous access to Volume features.
*/
@Delegate
VolumeAsyncApi getVolumeApiForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
/**
* Provides asynchronous access to VolumeType features.
*/
@Delegate
VolumeTypeAsyncApi getVolumeTypeApiForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
/**
* Provides asynchronous access to Snapshot features.
*/
@Delegate
SnapshotAsyncApi getSnapshotApiForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
}

View File

@ -0,0 +1,34 @@
/**
* 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.cinder.v1.config;
import org.jclouds.json.config.GsonModule;
import org.jclouds.json.config.GsonModule.DateAdapter;
import com.google.inject.AbstractModule;
/**
* @author Everett Toews
*/
public class CinderParserModule extends AbstractModule {
@Override
protected void configure() {
bind(DateAdapter.class).to(GsonModule.Iso8601DateAdapter.class);
}
}

View File

@ -0,0 +1,94 @@
/**
* 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.cinder.v1.config;
import java.net.URI;
import java.util.Map;
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.json.config.GsonModule.DateAdapter;
import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
import org.jclouds.openstack.cinder.v1.CinderApi;
import org.jclouds.openstack.cinder.v1.CinderAsyncApi;
import org.jclouds.openstack.cinder.v1.features.SnapshotApi;
import org.jclouds.openstack.cinder.v1.features.SnapshotAsyncApi;
import org.jclouds.openstack.cinder.v1.features.VolumeApi;
import org.jclouds.openstack.cinder.v1.features.VolumeAsyncApi;
import org.jclouds.openstack.cinder.v1.features.VolumeTypeApi;
import org.jclouds.openstack.cinder.v1.features.VolumeTypeAsyncApi;
import org.jclouds.openstack.cinder.v1.handlers.CinderErrorHandler;
import org.jclouds.openstack.v2_0.features.ExtensionApi;
import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi;
import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.config.RestClientModule;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import com.google.common.reflect.TypeToken;
import com.google.inject.Provides;
/**
* Configures the Cinder connection.
*
* @author Everett Toews
*/
@ConfiguresRestClient
public class CinderRestClientModule<S extends CinderApi, A extends CinderAsyncApi> extends RestClientModule<S, A> {
public static final Map<Class<?>, Class<?>> DELEGATE_MAP = ImmutableMap.<Class<?>, Class<?>> builder()
.put(ExtensionApi.class, ExtensionAsyncApi.class)
.put(VolumeApi.class, VolumeAsyncApi.class)
.put(VolumeTypeApi.class, VolumeTypeAsyncApi.class)
.put(SnapshotApi.class, SnapshotAsyncApi.class)
.build();
@SuppressWarnings("unchecked")
public CinderRestClientModule() {
super(TypeToken.class.cast(TypeToken.of(CinderApi.class)), TypeToken.class.cast(TypeToken.of(CinderAsyncApi.class)), DELEGATE_MAP);
}
protected CinderRestClientModule(TypeToken<S> syncClientType, TypeToken<A> asyncClientType, Map<Class<?>, Class<?>> sync2Async) {
super(syncClientType, asyncClientType, sync2Async);
}
@Override
protected void configure() {
bind(DateAdapter.class).to(Iso8601DateAdapter.class);
super.configure();
}
@Provides
@Singleton
public Multimap<URI, URI> aliases() {
return ImmutableMultimap.<URI, URI>builder().build();
}
@Override
protected void bindErrorHandlers() {
bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(CinderErrorHandler.class);
bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(CinderErrorHandler.class);
bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(CinderErrorHandler.class);
}
}

View File

@ -0,0 +1,244 @@
/*
* 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.cinder.v1.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import java.beans.ConstructorProperties;
import java.util.Date;
import javax.inject.Named;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
/**
* An Openstack Cinder Volume Snapshot.
*
* @author Everett Toews
*/
public class Snapshot {
public static Builder<?> builder() {
return new ConcreteBuilder();
}
public Builder<?> toBuilder() {
return new ConcreteBuilder().fromSnapshot(this);
}
public static abstract class Builder<T extends Builder<T>> {
protected abstract T self();
protected String id;
protected String volumeId;
protected Volume.Status status;
protected int size;
protected Date created;
protected String name;
protected String description;
/**
* @see Snapshot#getId()
*/
public T id(String id) {
this.id = id;
return self();
}
/**
* @see Snapshot#getVolumeId()
*/
public T volumeId(String volumeId) {
this.volumeId = volumeId;
return self();
}
/**
* @see Snapshot#getStatus()
*/
public T status(Volume.Status status) {
this.status = status;
return self();
}
/**
* @see Snapshot#getSize()
*/
public T size(int size) {
this.size = size;
return self();
}
/**
* @see Snapshot#getCreated()
*/
public T created(Date created) {
this.created = created;
return self();
}
/**
* @see Snapshot#getName()
*/
public T name(String name) {
this.name = name;
return self();
}
/**
* @see Snapshot#getDescription()
*/
public T description(String description) {
this.description = description;
return self();
}
public Snapshot build() {
return new Snapshot(id, volumeId, status, size, created, name, description);
}
public T fromSnapshot(Snapshot in) {
return this
.id(in.getId())
.volumeId(in.getVolumeId())
.status(in.getStatus())
.size(in.getSize())
.created(in.getCreated())
.name(in.getName())
.description(in.getDescription());
}
}
private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@Override
protected ConcreteBuilder self() {
return this;
}
}
private final String id;
@Named("volume_id")
private final String volumeId;
private final Volume.Status status;
private final int size;
@Named("created_at")
private final Date created;
@Named("display_name")
private final String name;
@Named("display_description")
private final String description;
@ConstructorProperties({
"id", "volume_id", "status", "size", "created_at", "display_name", "display_description"
})
protected Snapshot(String id, String volumeId, Volume.Status status, int size, @Nullable Date created, @Nullable String name, @Nullable String description) {
this.id = checkNotNull(id, "id");
this.volumeId = checkNotNull(volumeId, "volumeId");
this.status = checkNotNull(status, "status");
this.size = size;
this.created = created;
this.name = name;
this.description = description;
}
/**
* @return The id of this snapshot
*/
public String getId() {
return this.id;
}
/**
* @return The id of the Volume this snapshot was taken from
*/
public String getVolumeId() {
return this.volumeId;
}
/**
* @return The status of this snapshot
*/
public Volume.Status getStatus() {
return this.status;
}
/**
* @return The size in GB of the volume this snapshot was taken from
*/
public int getSize() {
return this.size;
}
/**
* @return The data the snapshot was taken
*/
@Nullable
public Date getCreated() {
return this.created;
}
/**
* @return The name of this snapshot - as displayed in the openstack console
*/
@Nullable
public String getName() {
return this.name;
}
/**
* @return The description of this snapshot - as displayed in the openstack console
*/
@Nullable
public String getDescription() {
return this.description;
}
@Override
public int hashCode() {
return Objects.hashCode(id, volumeId, status, size, created, name, description);
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Snapshot that = Snapshot.class.cast(obj);
return Objects.equal(this.id, that.id)
&& Objects.equal(this.volumeId, that.volumeId)
&& Objects.equal(this.status, that.status)
&& Objects.equal(this.size, that.size)
&& Objects.equal(this.created, that.created)
&& Objects.equal(this.name, that.name)
&& Objects.equal(this.description, that.description);
}
protected ToStringHelper string() {
return Objects.toStringHelper(this)
.add("id", id).add("volumeId", volumeId).add("status", status).add("size", size).add("created", created).add("name", name).add("description", description);
}
@Override
public String toString() {
return string().toString();
}
}

View File

@ -0,0 +1,352 @@
/*
* 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.cinder.v1.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import java.beans.ConstructorProperties;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import javax.inject.Named;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.base.CaseFormat;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
/**
* An Openstack Cinder Volume.
*
* @author Everett Toews
*/
public class Volume {
public static enum Status {
CREATING, AVAILABLE, ATTACHING, IN_USE, DELETING, ERROR, ERROR_DELETING, UNRECOGNIZED;
public String value() {
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, name());
}
@Override
public String toString() {
return value();
}
public static Status fromValue(String status) {
try {
return valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(status, "status")));
}
catch (IllegalArgumentException e) {
return UNRECOGNIZED;
}
}
}
public static Builder<?> builder() {
return new ConcreteBuilder();
}
public Builder<?> toBuilder() {
return new ConcreteBuilder().fromVolume(this);
}
public static abstract class Builder<T extends Builder<T>> {
protected abstract T self();
protected String id;
protected Volume.Status status;
protected int size;
protected String zone;
protected Date created;
protected Set<VolumeAttachment> attachments = ImmutableSet.of();
protected String volumeType;
protected String snapshotId;
protected String name;
protected String description;
protected Map<String, String> metadata = ImmutableMap.of();
/**
* @see Volume#getId()
*/
public T id(String id) {
this.id = id;
return self();
}
/**
* @see Volume#getStatus()
*/
public T status(Volume.Status status) {
this.status = status;
return self();
}
/**
* @see Volume#getSize()
*/
public T size(int size) {
this.size = size;
return self();
}
/**
* @see Volume#getZone()
*/
public T zone(String zone) {
this.zone = zone;
return self();
}
/**
* @see Volume#getCreated()
*/
public T created(Date created) {
this.created = created;
return self();
}
/**
* @see Volume#getAttachments()
*/
public T attachments(Set<VolumeAttachment> attachments) {
this.attachments = ImmutableSet.copyOf(checkNotNull(attachments, "attachments"));
return self();
}
public T attachments(VolumeAttachment... in) {
return attachments(ImmutableSet.copyOf(in));
}
/**
* @see Volume#getVolumeType()
*/
public T volumeType(String volumeType) {
this.volumeType = volumeType;
return self();
}
/**
* @see Volume#getSnapshotId()
*/
public T snapshotId(String snapshotId) {
this.snapshotId = snapshotId;
return self();
}
/**
* @see Volume#getName()
*/
public T name(String name) {
this.name = name;
return self();
}
/**
* @see Volume#getDescription()
*/
public T description(String description) {
this.description = description;
return self();
}
/**
* @see Volume#getMetadata()
*/
public T metadata(Map<String, String> metadata) {
this.metadata = ImmutableMap.copyOf(checkNotNull(metadata, "metadata"));
return self();
}
public Volume build() {
return new Volume(id, status, size, zone, created, attachments, volumeType, snapshotId, name, description, metadata);
}
public T fromVolume(Volume in) {
return this
.id(in.getId())
.status(in.getStatus())
.size(in.getSize())
.zone(in.getZone())
.created(in.getCreated())
.attachments(in.getAttachments())
.volumeType(in.getVolumeType())
.snapshotId(in.getSnapshotId())
.name(in.getName())
.description(in.getDescription())
.metadata(in.getMetadata());
}
}
private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@Override
protected ConcreteBuilder self() {
return this;
}
}
private final String id;
private final Volume.Status status;
private final int size;
@Named("availability_zone")
private final String zone;
@Named("created_at")
private final Date created;
private final Set<VolumeAttachment> attachments;
@Named("volume_type")
private final String volumeType;
@Named("snapshot_id")
private final String snapshotId;
@Named("display_name")
private final String name;
@Named("display_description")
private final String description;
private final Map<String, String> metadata;
@ConstructorProperties({
"id", "status", "size", "availability_zone", "created_at", "attachments", "volume_type", "snapshot_id", "display_name", "display_description", "metadata"
})
protected Volume(String id, Volume.Status status, int size, String zone, Date created, @Nullable Set<VolumeAttachment> attachments, @Nullable String volumeType, @Nullable String snapshotId, @Nullable String name, @Nullable String description, @Nullable Map<String, String> metadata) {
this.id = checkNotNull(id, "id");
this.status = checkNotNull(status, "status");
this.size = size;
this.zone = checkNotNull(zone, "zone");
this.created = checkNotNull(created, "created");
this.attachments = attachments == null ? ImmutableSet.<VolumeAttachment>of() : ImmutableSet.copyOf(attachments);
this.volumeType = volumeType;
this.snapshotId = snapshotId;
this.name = name;
this.description = description;
this.metadata = metadata == null ? ImmutableMap.<String, String>of() : ImmutableMap.copyOf(metadata);
}
/**
* @return the id of this volume
*/
public String getId() {
return this.id;
}
/**
* @return the status of this volume
*/
public Volume.Status getStatus() {
return this.status;
}
/**
* @return the size in GB of this volume
*/
public int getSize() {
return this.size;
}
/**
* @return the availabilityZone containing this volume
*/
public String getZone() {
return this.zone;
}
/**
* @return the time this volume was created
*/
public Date getCreated() {
return this.created;
}
/**
* @return the set of attachments (to Servers)
*/
public Set<VolumeAttachment> getAttachments() {
return this.attachments;
}
/**
* @return the type of this volume
*/
@Nullable
public String getVolumeType() {
return this.volumeType;
}
@Nullable
public String getSnapshotId() {
return this.snapshotId;
}
/**
* @return the name of this volume - as displayed in the openstack console
*/
@Nullable
public String getName() {
return this.name;
}
/**
* @return the description of this volume - as displayed in the openstack console
*/
@Nullable
public String getDescription() {
return this.description;
}
public Map<String, String> getMetadata() {
return this.metadata;
}
@Override
public int hashCode() {
return Objects.hashCode(id, status, size, zone, created, attachments, volumeType, snapshotId, name, description, metadata);
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Volume that = Volume.class.cast(obj);
return Objects.equal(this.id, that.id)
&& Objects.equal(this.status, that.status)
&& Objects.equal(this.size, that.size)
&& Objects.equal(this.zone, that.zone)
&& Objects.equal(this.created, that.created)
&& Objects.equal(this.attachments, that.attachments)
&& Objects.equal(this.volumeType, that.volumeType)
&& Objects.equal(this.snapshotId, that.snapshotId)
&& Objects.equal(this.name, that.name)
&& Objects.equal(this.description, that.description)
&& Objects.equal(this.metadata, that.metadata);
}
protected ToStringHelper string() {
return Objects.toStringHelper(this)
.add("id", id).add("status", status).add("size", size).add("zone", zone).add("created", created).add("attachments", attachments).add("volumeType", volumeType).add("snapshotId", snapshotId).add("name", name).add("description", description).add("metadata", metadata);
}
@Override
public String toString() {
return string().toString();
}
}

View File

@ -0,0 +1,178 @@
/*
* 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.cinder.v1.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import java.beans.ConstructorProperties;
import javax.inject.Named;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
/**
* An OpenStack Cinder Volume Attachment (describes how Volumes are attached to Servers).
*/
public class VolumeAttachment {
public static Builder<?> builder() {
return new ConcreteBuilder();
}
public Builder<?> toBuilder() {
return new ConcreteBuilder().fromVolumeAttachment(this);
}
public abstract static class Builder<T extends Builder<T>> {
protected abstract T self();
protected String id;
protected String volumeId;
protected String serverId;
protected String device;
/**
* @see VolumeAttachment#getId()
*/
public T id(String id) {
this.id = id;
return self();
}
/**
* @see VolumeAttachment#getVolumeId()
*/
public T volumeId(String volumeId) {
this.volumeId = volumeId;
return self();
}
/**
* @see VolumeAttachment#getServerId()
*/
public T serverId(String serverId) {
this.serverId = serverId;
return self();
}
/**
* @see VolumeAttachment#getDevice()
*/
public T device(String device) {
this.device = device;
return self();
}
public VolumeAttachment build() {
return new VolumeAttachment(id, volumeId, serverId, device);
}
public T fromVolumeAttachment(VolumeAttachment in) {
return this
.id(in.getId())
.volumeId(in.getVolumeId())
.serverId(in.getServerId())
.device(in.getDevice());
}
}
private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@Override
protected ConcreteBuilder self() {
return this;
}
}
private final String id;
@Named("volume_id")
private final String volumeId;
@Named("server_id")
private final String serverId;
private final String device;
@ConstructorProperties({
"id", "volume_id", "server_id", "device"
})
protected VolumeAttachment(String id, String volumeId, @Nullable String serverId, @Nullable String device) {
this.id = checkNotNull(id, "id");
this.volumeId = checkNotNull(volumeId, "volumeId");
this.serverId = serverId;
this.device = device;
}
/**
* @return the attachment id (typically the same as #getVolumeId())
*/
public String getId() {
return this.id;
}
/**
* @return the id of the volume attached
*/
public String getVolumeId() {
return this.volumeId;
}
/**
* @return the id of the server the volume is attached to
*/
@Nullable
public String getServerId() {
return this.serverId;
}
/**
* @return the device name (e.g. "/dev/vdc")
*/
@Nullable
public String getDevice() {
return this.device;
}
@Override
public int hashCode() {
return Objects.hashCode(id, volumeId, serverId, device);
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
VolumeAttachment that = VolumeAttachment.class.cast(obj);
return Objects.equal(this.id, that.id)
&& Objects.equal(this.volumeId, that.volumeId)
&& Objects.equal(this.serverId, that.serverId)
&& Objects.equal(this.device, that.device);
}
protected ToStringHelper string() {
return Objects.toStringHelper(this)
.add("id", id).add("volumeId", volumeId).add("serverId", serverId).add("device", device);
}
@Override
public String toString() {
return string().toString();
}
}

View File

@ -0,0 +1,194 @@
/*
* 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.cinder.v1.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import java.beans.ConstructorProperties;
import java.util.Date;
import java.util.Map;
import javax.inject.Named;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
/**
* An Openstack Cinder Volume Type.
*
* @author Everett Toews
*/
public class VolumeType {
public static Builder<?> builder() {
return new ConcreteBuilder();
}
public Builder<?> toBuilder() {
return new ConcreteBuilder().fromVolumeType(this);
}
public static abstract class Builder<T extends Builder<T>> {
protected abstract T self();
protected String id;
protected String name;
protected Date created;
protected Date updated;
protected Map<String, String> extraSpecs = ImmutableMap.of();
/**
* @see VolumeType#getId()
*/
public T id(String id) {
this.id = id;
return self();
}
/**
* @see VolumeType#getName()
*/
public T name(String name) {
this.name = name;
return self();
}
/**
* @see VolumeType#getCreated()
*/
public T created(Date created) {
this.created = created;
return self();
}
/**
* @see VolumeType#getUpdated()
*/
public T updated(Date updated) {
this.updated = updated;
return self();
}
/**
* @see VolumeType#getExtraSpecs()
*/
public T extraSpecs(Map<String, String> extraSpecs) {
this.extraSpecs = ImmutableMap.copyOf(checkNotNull(extraSpecs, "extraSpecs"));
return self();
}
public VolumeType build() {
return new VolumeType(id, name, created, updated, extraSpecs);
}
public T fromVolumeType(VolumeType in) {
return this
.id(in.getId())
.name(in.getName())
.created(in.getCreated().orNull())
.updated(in.getUpdated().orNull())
.extraSpecs(in.getExtraSpecs());
}
}
private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@Override
protected ConcreteBuilder self() {
return this;
}
}
private final String id;
private final String name;
@Named("created_at")
private final Optional<Date> created;
@Named("updated_at")
private final Optional<Date> updated;
@Named("extra_specs")
private final Map<String, String> extraSpecs;
@ConstructorProperties({
"id", "name", "created_at", "updated_at", "extra_specs"
})
protected VolumeType(String id, String name, @Nullable Date created, @Nullable Date updated, Map<String, String> extraSpecs) {
this.id = checkNotNull(id, "id");
this.name = checkNotNull(name, "name");
this.created = Optional.fromNullable(created);
this.updated = Optional.fromNullable(updated);
this.extraSpecs = ImmutableMap.copyOf(checkNotNull(extraSpecs, "extraSpecs"));
}
public String getId() {
return this.id;
}
public String getName() {
return this.name;
}
/**
* The Date the VolumeType was created
*/
public Optional<Date> getCreated() {
return this.created;
}
/**
* The Date the VolumeType as last updated - absent if no updates have taken place
*/
public Optional<Date> getUpdated() {
return this.updated;
}
public Map<String, String> getExtraSpecs() {
return this.extraSpecs;
}
@Override
public int hashCode() {
return Objects.hashCode(id, name, created, updated, extraSpecs);
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
VolumeType that = VolumeType.class.cast(obj);
return Objects.equal(this.id, that.id)
&& Objects.equal(this.name, that.name)
&& Objects.equal(this.created, that.created)
&& Objects.equal(this.updated, that.updated)
&& Objects.equal(this.extraSpecs, that.extraSpecs);
}
protected ToStringHelper string() {
return Objects.toStringHelper(this)
.add("id", id).add("name", name).add("created", created).add("updated", updated).add("extraSpecs", extraSpecs);
}
@Override
public String toString() {
return string().toString();
}
}

View File

@ -0,0 +1,58 @@
package org.jclouds.openstack.cinder.v1.features;
import java.util.concurrent.TimeUnit;
import org.jclouds.concurrent.Timeout;
import org.jclouds.openstack.cinder.v1.domain.Snapshot;
import org.jclouds.openstack.cinder.v1.options.CreateSnapshotOptions;
import com.google.common.collect.FluentIterable;
/**
* Provides synchronous access to Volume Snapshots via their REST API.
*
* @see SnapshotAsyncApi
* @see <a href="http://api.openstack.org/">API Doc</a>
* @author Everett Toews
*/
@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS)
public interface SnapshotApi {
/**
* Returns a summary list of Snapshots.
*
* @return The list of Snapshots
*/
FluentIterable<? extends Snapshot> list();
/**
* Returns a detailed list of Snapshots.
*
* @return The list of Snapshots
*/
FluentIterable<? extends Snapshot> listInDetail();
/**
* Return data about the given Snapshot.
*
* @param snapshotId Id of the Snapshot
* @return Details of a specific Snapshot
*/
Snapshot get(String snapshotId);
/**
* Creates a new Snapshot
*
* @param volumeId The Volume Id from which to create the Snapshot
* @param options See CreateSnapshotOptions
* @return The new Snapshot
*/
Snapshot create(String volumeId, CreateSnapshotOptions... options);
/**
* Delete a Snapshot.
*
* @param snapshotId Id of the Snapshot
* @return true if successful, false otherwise
*/
boolean delete(String snapshotId);
}

View File

@ -0,0 +1,105 @@
/**
* 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.cinder.v1.features;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jclouds.openstack.cinder.v1.domain.Snapshot;
import org.jclouds.openstack.cinder.v1.options.CreateSnapshotOptions;
import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.MapBinder;
import org.jclouds.rest.annotations.PayloadParam;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.SelectJson;
import org.jclouds.rest.annotations.SkipEncoding;
import org.jclouds.rest.functions.ReturnEmptyFluentIterableOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import com.google.common.collect.FluentIterable;
import com.google.common.util.concurrent.ListenableFuture;
/**
* Provides asynchronous access to Snapshots.
*
* @see SnapshotApi
* @see <a href="http://api.openstack.org/">API Doc</a>
* @author Everett Toews
*/
@SkipEncoding({'/', '='})
@RequestFilters(AuthenticateRequest.class)
public interface SnapshotAsyncApi {
/**
* @see SnapshotApi#list()
*/
@GET
@Path("/snapshots")
@SelectJson("snapshots")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnEmptyFluentIterableOnNotFoundOr404.class)
ListenableFuture<? extends FluentIterable<? extends Snapshot>> list();
/**
* @see SnapshotApi#listInDetail()
*/
@GET
@Path("/snapshots/detail")
@SelectJson("snapshots")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnEmptyFluentIterableOnNotFoundOr404.class)
ListenableFuture<? extends FluentIterable<? extends Snapshot>> listInDetail();
/**
* @see SnapshotApi#get(String)
*/
@GET
@Path("/snapshots/{id}")
@SelectJson("snapshot")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<? extends Snapshot> get(@PathParam("id") String snapshotId);
/**
* @see SnapshotApi#create(String, CreateSnapshotOptions...)
*/
@POST
@Path("/snapshots")
@SelectJson("snapshot")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@MapBinder(CreateSnapshotOptions.class)
ListenableFuture<? extends Snapshot> create(@PayloadParam("volume_id") String volumeId, CreateSnapshotOptions... options);
/**
* @see SnapshotApi#delete(String)
*/
@DELETE
@Path("/snapshots/{id}")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
ListenableFuture<Boolean> delete(@PathParam("id") String snapshotId);
}

View File

@ -0,0 +1,61 @@
package org.jclouds.openstack.cinder.v1.features;
import java.util.concurrent.TimeUnit;
import org.jclouds.concurrent.Timeout;
import org.jclouds.openstack.cinder.v1.domain.Volume;
import org.jclouds.openstack.cinder.v1.options.CreateVolumeOptions;
import com.google.common.collect.FluentIterable;
/**
* Provides synchronous access to Volumes.
*
* This API strictly handles creating and managing Volumes. To attach a Volume to a Server you need to use the
* @see VolumeAttachmentApi
*
* @see VolumeAsyncApi
* @see <a href="http://api.openstack.org/">API Doc</a>
* @author Everett Toews
*/
@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS)
public interface VolumeApi {
/**
* Returns a summary list of Volumes.
*
* @return The list of Volumes
*/
FluentIterable<? extends Volume> list();
/**
* Returns a detailed list of Volumes.
*
* @return The list of Volumes
*/
FluentIterable<? extends Volume> listInDetail();
/**
* Return data about the given Volume.
*
* @param volumeId Id of the Volume
* @return Details of a specific Volume
*/
Volume get(String volumeId);
/**
* Creates a new Volume
*
* @param volumeId Id of the Volume
* @param options See CreateVolumeOptions
* @return The new Volume
*/
Volume create(int sizeGB, CreateVolumeOptions... options);
/**
* Delete a Volume.
*
* @param volumeId Id of the Volume
* @return true if successful, false otherwise
*/
boolean delete(String volumeId);
}

View File

@ -0,0 +1,105 @@
/**
* 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.cinder.v1.features;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jclouds.openstack.cinder.v1.domain.Volume;
import org.jclouds.openstack.cinder.v1.options.CreateVolumeOptions;
import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.MapBinder;
import org.jclouds.rest.annotations.PayloadParam;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.SelectJson;
import org.jclouds.rest.annotations.SkipEncoding;
import org.jclouds.rest.functions.ReturnEmptyFluentIterableOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import com.google.common.collect.FluentIterable;
import com.google.common.util.concurrent.ListenableFuture;
/**
* Provides asynchronous access to Volumes.
*
* @see VolumeApi
* @see <a href="http://api.openstack.org/">API Doc</a>
* @author Everett Toews
*/
@SkipEncoding({'/', '='})
@RequestFilters(AuthenticateRequest.class)
public interface VolumeAsyncApi {
/**
* @see VolumeApi#list()
*/
@GET
@Path("/volumes")
@SelectJson("volumes")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnEmptyFluentIterableOnNotFoundOr404.class)
ListenableFuture<? extends FluentIterable<? extends Volume>> list();
/**
* @see VolumeApi#listInDetail()
*/
@GET
@Path("/volumes/detail")
@SelectJson("volumes")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnEmptyFluentIterableOnNotFoundOr404.class)
ListenableFuture<? extends FluentIterable<? extends Volume>> listInDetail();
/**
* @see VolumeApi#get(String)
*/
@GET
@Path("/volumes/{id}")
@SelectJson("volume")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<? extends Volume> get(@PathParam("id") String volumeId);
/**
* @see VolumeApi#create(int, CreateVolumeOptions...)
*/
@POST
@Path("/volumes")
@SelectJson("volume")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@MapBinder(CreateVolumeOptions.class)
ListenableFuture<? extends Volume> create(@PayloadParam("size") int sizeGB, CreateVolumeOptions... options);
/**
* @see VolumeApi#delete(String)
*/
@DELETE
@Path("/volumes/{id}")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
ListenableFuture<Boolean> delete(@PathParam("id") String volumeId);
}

View File

@ -0,0 +1,33 @@
package org.jclouds.openstack.cinder.v1.features;
import java.util.concurrent.TimeUnit;
import org.jclouds.concurrent.Timeout;
import org.jclouds.openstack.cinder.v1.domain.VolumeType;
import com.google.common.collect.FluentIterable;
/**
* Provides synchronous access to Volumes via their REST API.
*
* @see VolumeAsyncApi
* @see <a href="http://api.openstack.org/">API Doc</a>
* @author Everett Toews
*/
@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS)
public interface VolumeTypeApi {
/**
* Returns a summary list of VolumeTypes.
*
* @return The list of VolumeTypes
*/
FluentIterable<? extends VolumeType> list();
/**
* Return data about the given VolumeType.
*
* @param volumeTypeId Id of the VolumeType
* @return Details of a specific VolumeType
*/
VolumeType get(String volumeTypeId);
}

View File

@ -0,0 +1,68 @@
/**
* 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.cinder.v1.features;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.MediaType;
import org.jclouds.openstack.cinder.v1.domain.VolumeType;
import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.SelectJson;
import org.jclouds.rest.annotations.SkipEncoding;
import org.jclouds.rest.functions.ReturnEmptyFluentIterableOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import com.google.common.collect.FluentIterable;
import com.google.common.util.concurrent.ListenableFuture;
/**
* Provides asynchronous access to VolumeTypes.
*
* @see VolumeTypeApi
* @see <a href="http://api.openstack.org/">API Doc</a>
* @author Everett Toews
*/
@SkipEncoding({'/', '='})
@RequestFilters(AuthenticateRequest.class)
public interface VolumeTypeAsyncApi {
/**
* @see VolumeTypeApi#list()
*/
@GET
@Path("/types")
@SelectJson("volume_types")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnEmptyFluentIterableOnNotFoundOr404.class)
ListenableFuture<? extends FluentIterable<? extends VolumeType>> list();
/**
* @see VolumeTypeApi#get(String)
*/
@GET
@Path("/types/{id}")
@SelectJson("volume_type")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<? extends VolumeType> get(@PathParam("id") String volumeTypeId);
}

View File

@ -0,0 +1,73 @@
/**
* 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.cinder.v1.handlers;
import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
import javax.inject.Singleton;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpResponseException;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.InsufficientResourcesException;
import org.jclouds.rest.ResourceNotFoundException;
/**
* This will parse and set an appropriate exception on the command object.
*
* @author Everett Toews
*
*/
@Singleton
public class CinderErrorHandler implements HttpErrorHandler {
public void handleError(HttpCommand command, HttpResponse response) {
// it is important to always read fully and close streams
byte[] data = closeClientButKeepContentStream(response);
String message = data != null ? new String(data) : null;
Exception exception = message != null ? new HttpResponseException(command, response, message)
: new HttpResponseException(command, response);
message = message != null ? message : String.format("%s -> %s", command.getCurrentRequest().getRequestLine(),
response.getStatusLine());
switch (response.getStatusCode()) {
case 400:
if (message.contains("quota exceeded"))
exception = new InsufficientResourcesException(message, exception);
else if (message.contains("Invalid volume"))
exception = new IllegalStateException(message, exception);
break;
case 401:
case 403:
exception = new AuthorizationException(message, exception);
break;
case 404:
if (!command.getCurrentRequest().getMethod().equals("DELETE")) {
exception = new ResourceNotFoundException(message, exception);
}
break;
case 413:
exception = new InsufficientResourcesException(message, exception);
break;
}
command.setException(exception);
}
}

View File

@ -0,0 +1,140 @@
/**
* 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.cinder.v1.options;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Objects.toStringHelper;
import java.util.Map;
import javax.inject.Inject;
import org.jclouds.http.HttpRequest;
import org.jclouds.rest.MapBinder;
import org.jclouds.rest.binders.BindToJsonPayload;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
/**
* @author Adam Lowe
*/
public class CreateSnapshotOptions implements MapBinder {
public static final CreateSnapshotOptions NONE = new CreateSnapshotOptions();
@Inject
private BindToJsonPayload jsonBinder;
private String name;
private String description;
private boolean force = false;
@Override
public <R extends HttpRequest> R bindToRequest(R request, Map<String, Object> postParams) {
Map<String, Object> data = Maps.newHashMap(postParams);
if (name != null)
data.put("display_name", name);
if (description != null)
data.put("display_description", description);
if (force)
data.put("force", "true");
return jsonBinder.bindToRequest(request, ImmutableMap.of("snapshot", data));
}
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object toBind) {
throw new IllegalStateException("CreateSnapshot is a POST operation");
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof CreateSnapshotOptions)) return false;
final CreateSnapshotOptions other = CreateSnapshotOptions.class.cast(object);
return equal(name, other.name) && equal(description, other.description);
}
@Override
public int hashCode() {
return Objects.hashCode(name, description);
}
protected ToStringHelper string() {
return toStringHelper("").add("name", name).add("description", description);
}
@Override
public String toString() {
return string().toString();
}
public CreateSnapshotOptions name(String name) {
this.name = name;
return this;
}
public CreateSnapshotOptions description(String description) {
this.description = description;
return this;
}
public CreateSnapshotOptions force() {
this.force = true;
return this;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public boolean isForce() {
return force;
}
public static class Builder {
/**
* @see CreateSnapshotOptions#getName()
*/
public static CreateSnapshotOptions name(String name) {
return new CreateSnapshotOptions().name(name);
}
/**
* @see CreateSnapshotOptions#getDescription()
*/
public static CreateSnapshotOptions description(String description) {
return new CreateSnapshotOptions().description(description);
}
/**
* @see CreateSnapshotOptions#isForce()
*/
public static CreateSnapshotOptions force() {
return new CreateSnapshotOptions().force();
}
}
}

View File

@ -0,0 +1,240 @@
/**
* 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.cinder.v1.options;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Objects.toStringHelper;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Map;
import java.util.Map.Entry;
import javax.inject.Inject;
import org.jclouds.http.HttpRequest;
import org.jclouds.openstack.cinder.v1.features.VolumeTypeApi;
import org.jclouds.rest.MapBinder;
import org.jclouds.rest.binders.BindToJsonPayload;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
/**
* @author Adam Lowe
*/
public class CreateVolumeOptions implements MapBinder {
public static final CreateVolumeOptions NONE = new CreateVolumeOptions();
@Inject
private BindToJsonPayload jsonBinder;
protected String name;
protected String description;
protected String volumeType;
protected String availabilityZone;
protected String snapshotId;
protected Map<String, String> metadata = ImmutableMap.of();
@Override
public <R extends HttpRequest> R bindToRequest(R request, Map<String, Object> postParams) {
Map<String, Object> image = Maps.newHashMap();
image.putAll(postParams);
if (name != null)
image.put("display_name", name);
if (description != null)
image.put("display_description", description);
if (volumeType != null)
image.put("volume_type", volumeType);
if (availabilityZone != null)
image.put("availability_zone", availabilityZone);
if (snapshotId != null)
image.put("snapshot_id", snapshotId);
if (!metadata.isEmpty())
image.put("metadata", metadata);
return jsonBinder.bindToRequest(request, ImmutableMap.of("volume", image));
}
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object toBind) {
throw new IllegalStateException("CreateVolume is a POST operation");
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof CreateVolumeOptions)) return false;
final CreateVolumeOptions other = CreateVolumeOptions.class.cast(object);
return equal(volumeType, other.volumeType) && equal(availabilityZone, other.availabilityZone) && equal(snapshotId, other.snapshotId)
&& equal(name, other.name) && equal(description, other.description) && equal(metadata, other.metadata);
}
@Override
public int hashCode() {
return Objects.hashCode(volumeType, availabilityZone, snapshotId, name, description, metadata);
}
protected ToStringHelper string() {
return toStringHelper("").add("volumeType", volumeType).add("availabilityZone", availabilityZone)
.add("snapshotId", snapshotId).add("name", name).add("description", description).add("metadata", metadata);
}
@Override
public String toString() {
return string().toString();
}
/**
* Custom cloud server metadata can also be supplied at launch time. This
* metadata is stored in the API system where it is retrievable by querying
* the API for server status. The maximum size of the metadata key and value
* is each 255 bytes and the maximum number of key-value pairs that can be
* supplied per volume is 5.
*/
public CreateVolumeOptions metadata(Map<String, String> metadata) {
checkNotNull(metadata, "metadata");
checkArgument(metadata.size() <= 5,
"you cannot have more then 5 metadata values. You specified: " + metadata.size());
for (Entry<String, String> entry : metadata.entrySet()) {
checkArgument(
entry.getKey().getBytes().length < 255,
String.format("maximum length of metadata key is 255 bytes. Key specified %s is %d bytes",
entry.getKey(), entry.getKey().getBytes().length));
checkArgument(entry.getKey().getBytes().length < 255, String.format(
"maximum length of metadata value is 255 bytes. Value specified for %s (%s) is %d bytes",
entry.getKey(), entry.getValue(), entry.getValue().getBytes().length));
}
this.metadata = ImmutableMap.copyOf(metadata);
return this;
}
/**
* @param name The name of the Volume
*/
public CreateVolumeOptions name(String name) {
this.name = name;
return this;
}
/**
* @param description A description of the Volume
*/
public CreateVolumeOptions description(String description) {
this.description = description;
return this;
}
/**
* @see VolumeTypeApi#list()
*
* @param volumeType The type of Volume to create
*/
public CreateVolumeOptions volumeType(String volumeType) {
this.volumeType = volumeType;
return this;
}
/**
* @param availabilityZone The optional availability zone in which to create a Volume
*/
public CreateVolumeOptions availabilityZone(String availabilityZone) {
this.availabilityZone = availabilityZone;
return this;
}
/**
* @param snapshotId The optional snapshot from which to create a Volume
*/
public CreateVolumeOptions snapshotId(String snapshotId) {
this.snapshotId = snapshotId;
return this;
}
public String getVolumeType() {
return volumeType;
}
public String getAvailabilityZone() {
return availabilityZone;
}
public String getSnapshotId() {
return snapshotId;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public Map<String, String> getMetadata() {
return metadata;
}
public static class Builder {
/**
* @see CreateVolumeOptions#getName()
*/
public static CreateVolumeOptions name(String name) {
return new CreateVolumeOptions().name(name);
}
/**
* @see CreateVolumeOptions#getDescription()
*/
public static CreateVolumeOptions description(String description) {
return new CreateVolumeOptions().description(description);
}
/**
* @see CreateVolumeOptions#getVolumeType()
*/
public static CreateVolumeOptions volumeType(String volumeType) {
return new CreateVolumeOptions().volumeType(volumeType);
}
/**
* @see CreateVolumeOptions#getAvailabilityZone()
*/
public static CreateVolumeOptions availabilityZone(String availabilityZone) {
return new CreateVolumeOptions().availabilityZone(availabilityZone);
}
/**
* @see CreateVolumeOptions#getSnapshotId()
*/
public static CreateVolumeOptions snapshotId(String snapshotId) {
return new CreateVolumeOptions().snapshotId(snapshotId);
}
/**
* @see CreateVolumeOptions#getMetadata()
*/
public static CreateVolumeOptions metadata(Map<String, String> metadata) {
return new CreateVolumeOptions().metadata(metadata);
}
}
}

View File

@ -0,0 +1 @@
org.jclouds.openstack.cinder.v1.CinderApiMetadata

View File

@ -0,0 +1,303 @@
/**
* 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.cinder.v1.features;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import java.net.URI;
import java.util.Set;
import javax.ws.rs.core.MediaType;
import org.jclouds.date.DateService;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.http.HttpResponse;
import org.jclouds.openstack.cinder.v1.domain.Snapshot;
import org.jclouds.openstack.cinder.v1.domain.Volume;
import org.jclouds.openstack.cinder.v1.internal.BaseCinderApiExpectTest;
import org.jclouds.openstack.cinder.v1.options.CreateSnapshotOptions;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.ResourceNotFoundException;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
/**
* Tests SnapshotApi Guice wiring and parsing
*
* @author Everett Toews
*/
@Test(groups = "unit", testName = "SnapshotApiExpectTest")
public class SnapshotApiExpectTest extends BaseCinderApiExpectTest {
private DateService dateService = new SimpleDateFormatDateService();
public void testListSnapshots() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/snapshots");
SnapshotApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).build(),
HttpResponse.builder().statusCode(200).payload(payloadFromResource("/snapshot_list_simple.json")).build()
).getSnapshotApiForZone("RegionOne");
Set<? extends Snapshot> snapshots = api.list().toImmutableSet();
assertEquals(snapshots, ImmutableSet.of(testSnapshot()));
}
public void testListSnapshotsFail() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/snapshots");
SnapshotApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).build(),
HttpResponse.builder().statusCode(404).build()
).getSnapshotApiForZone("RegionOne");
Set<? extends Snapshot> snapshots = api.list().toImmutableSet();
assertTrue(snapshots.isEmpty());
}
public void testListSnapshotsInDetail() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/snapshots/detail");
SnapshotApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).build(),
HttpResponse.builder().statusCode(200).payload(payloadFromResource("/snapshot_list_details.json")).build()
).getSnapshotApiForZone("RegionOne");
Set<? extends Snapshot> snapshots = api.listInDetail().toImmutableSet();
assertEquals(snapshots, ImmutableSet.of(testSnapshot()));
// double-check individual fields
Snapshot snappy = Iterables.getOnlyElement(snapshots);
assertEquals(snappy.getId(), "67d03df1-ce5d-4ba7-adbe-492ceb80170b");
assertEquals(snappy.getVolumeId(), "ea6f70ef-2784-40b9-9d14-d7f33c507c3f");
assertEquals(snappy.getStatus(), Volume.Status.AVAILABLE);
assertEquals(snappy.getDescription(), "jclouds test snapshot");
assertEquals(snappy.getName(), "jclouds-test-snapshot");
assertEquals(snappy.getSize(), 1);
}
public void testListSnapshotsInDetailFail() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/snapshots/detail");
SnapshotApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).build(),
HttpResponse.builder().statusCode(404).build()
).getSnapshotApiForZone("RegionOne");
Set<? extends Snapshot> snapshots = api.listInDetail().toImmutableSet();
assertTrue(snapshots.isEmpty());
}
public void testGetSnapshot() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/snapshots/67d03df1-ce5d-4ba7-adbe-492ceb80170b");
SnapshotApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).build(),
HttpResponse.builder().statusCode(200).payload(payloadFromResource("/snapshot_get.json")).build()
).getSnapshotApiForZone("RegionOne");
Snapshot snapshot = api.get("67d03df1-ce5d-4ba7-adbe-492ceb80170b");
assertEquals(snapshot, testSnapshot());
}
public void testGetSnapshotFail() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/snapshots/67d03df1-ce5d-4ba7-adbe-492ceb80170b");
SnapshotApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).build(),
HttpResponse.builder().statusCode(404).build()
).getSnapshotApiForZone("RegionOne");
assertNull(api.get("67d03df1-ce5d-4ba7-adbe-492ceb80170b"));
}
public void testCreateSnapshot() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/snapshots");
SnapshotApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint)
.method("POST")
.payload(payloadFromResourceWithContentType("/snapshot_create.json", MediaType.APPLICATION_JSON))
.build(),
HttpResponse.builder().statusCode(200).payload(payloadFromResource("/snapshot_create_response.json")).build()
).getSnapshotApiForZone("RegionOne");
CreateSnapshotOptions options = CreateSnapshotOptions.Builder
.name("jclouds-test-snapshot")
.description("jclouds test snapshot")
.force();
Snapshot snapshot = api.create("ea6f70ef-2784-40b9-9d14-d7f33c507c3f", options);
assertEquals(snapshot, testSnapshotCreate());
}
@Test(expectedExceptions = ResourceNotFoundException.class)
public void testCreateSnapshotVolumeNotFoundFail() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/snapshots");
SnapshotApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint)
.method("POST")
.payload(payloadFromResourceWithContentType("/snapshot_create.json", MediaType.APPLICATION_JSON))
.build(),
HttpResponse.builder().statusCode(404).build()
).getSnapshotApiForZone("RegionOne");
CreateSnapshotOptions options = CreateSnapshotOptions.Builder
.name("jclouds-test-snapshot")
.description("jclouds test snapshot")
.force();
api.create("ea6f70ef-2784-40b9-9d14-d7f33c507c3f", options);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testCreateSnapshotVolumeIllegalStateFail() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/snapshots");
SnapshotApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint)
.method("POST")
.payload(payloadFromResourceWithContentType("/snapshot_create.json", MediaType.APPLICATION_JSON))
.build(),
HttpResponse.builder()
.statusCode(400)
.payload("{\"badRequest\": {\"message\": \"Invalid volume: must be available\", \"code\": 400}}")
.build()
).getSnapshotApiForZone("RegionOne");
CreateSnapshotOptions options = CreateSnapshotOptions.Builder
.name("jclouds-test-snapshot")
.description("jclouds test snapshot")
.force();
api.create("ea6f70ef-2784-40b9-9d14-d7f33c507c3f", options);
}
@Test(expectedExceptions = AuthorizationException.class)
public void testCreateSnapshotFail() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/snapshots");
SnapshotApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint)
.method("POST")
.payload(payloadFromResourceWithContentType("/snapshot_create.json", MediaType.APPLICATION_JSON))
.build(),
HttpResponse.builder().statusCode(401).build()
).getSnapshotApiForZone("RegionOne");
CreateSnapshotOptions options = CreateSnapshotOptions.Builder
.name("jclouds-test-snapshot")
.description("jclouds test snapshot")
.force();
api.create("ea6f70ef-2784-40b9-9d14-d7f33c507c3f", options);
}
public void testDeleteSnapshot() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/snapshots/67d03df1-ce5d-4ba7-adbe-492ceb80170b");
SnapshotApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).method("DELETE").build(),
HttpResponse.builder().statusCode(200).build()
).getSnapshotApiForZone("RegionOne");
assertTrue(api.delete("67d03df1-ce5d-4ba7-adbe-492ceb80170b"));
}
@Test(expectedExceptions = AuthorizationException.class)
public void testDeleteSnapshotFail() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/snapshots/67d03df1-ce5d-4ba7-adbe-492ceb80170b");
SnapshotApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).method("DELETE").build(),
HttpResponse.builder().statusCode(401).build()
).getSnapshotApiForZone("RegionOne");
api.delete("67d03df1-ce5d-4ba7-adbe-492ceb80170b");
}
public void testDeleteSnapshotNotFoundFail() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/snapshots/67d03df1-ce5d-4ba7-adbe-492ceb80170b");
SnapshotApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).method("DELETE").build(),
HttpResponse.builder().statusCode(404).build()
).getSnapshotApiForZone("RegionOne");
assertFalse(api.delete("67d03df1-ce5d-4ba7-adbe-492ceb80170b"));
}
@Test(expectedExceptions = IllegalStateException.class)
public void testDeleteSnapshotIllegalStateFail() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/snapshots/67d03df1-ce5d-4ba7-adbe-492ceb80170b");
SnapshotApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).method("DELETE").build(),
HttpResponse.builder()
.statusCode(400)
.payload("{\"badRequest\": {\"message\": \"Invalid volume: Volume Snapshot status must be available or error\", \"code\": 400}}")
.build()
).getSnapshotApiForZone("RegionOne");
api.delete("67d03df1-ce5d-4ba7-adbe-492ceb80170b");
}
protected Snapshot testSnapshotCreate() {
return Snapshot.builder()
.id("67d03df1-ce5d-4ba7-adbe-492ceb80170b")
.volumeId("ea6f70ef-2784-40b9-9d14-d7f33c507c3f")
.description("jclouds test snapshot")
.status(Volume.Status.CREATING)
.name("jclouds-test-snapshot")
.size(1)
.created(dateService.iso8601DateParse("2012-11-02T16:23:27.000000"))
.build();
}
protected Snapshot testSnapshot() {
return Snapshot.builder()
.id("67d03df1-ce5d-4ba7-adbe-492ceb80170b")
.volumeId("ea6f70ef-2784-40b9-9d14-d7f33c507c3f")
.description("jclouds test snapshot")
.status(Volume.Status.AVAILABLE)
.name("jclouds-test-snapshot")
.size(1)
.created(dateService.iso8601DateParse("2012-11-02T16:23:27.000000"))
.build();
}
}

View File

@ -0,0 +1,210 @@
/**
* 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.cinder.v1.features;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import java.util.Set;
import org.jclouds.openstack.cinder.v1.domain.Snapshot;
import org.jclouds.openstack.cinder.v1.domain.Volume;
import org.jclouds.openstack.cinder.v1.internal.BaseCinderApiLiveTest;
import org.jclouds.openstack.cinder.v1.options.CreateSnapshotOptions;
import org.jclouds.openstack.cinder.v1.options.CreateVolumeOptions;
import org.jclouds.predicates.RetryablePredicate;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
/**
* Tests behavior of VolumeApi
*
* @author Everett Toews
*/
@Test(groups = "live", testName = "VolumeApiLiveTest", singleThreaded = true)
public class VolumeAndSnapshotApiLiveTest extends BaseCinderApiLiveTest {
private static final String name = System.getProperty("user.name").replace('.','-').toLowerCase();
private String zone;
private VolumeApi volumeApi;
private SnapshotApi snapshotApi;
private Volume testVolume;
private Snapshot testSnapshot;
@BeforeClass(groups = {"integration", "live"})
@Override
public void setupContext() {
super.setupContext();
zone = Iterables.getLast(cinder.getApi().getConfiguredZones(), "nova");
volumeApi = cinder.getApi().getVolumeApiForZone(zone);
snapshotApi = cinder.getApi().getSnapshotApiForZone(zone);
}
@AfterClass(groups = { "integration", "live" })
@Override
protected void tearDownContext() {
if (testSnapshot != null) {
assertTrue(snapshotApi.delete(testSnapshot.getId()));
assertTrue(new RetryablePredicate<SnapshotApi>(new Predicate<SnapshotApi>() {
@Override
public boolean apply(SnapshotApi snapshotApi) {
return snapshotApi.get(testSnapshot.getId()) == null;
}
}, 30 * 1000L).apply(snapshotApi));
}
if (testVolume != null) {
assertTrue(volumeApi.delete(testVolume.getId()));
assertTrue(new RetryablePredicate<VolumeApi>(new Predicate<VolumeApi>() {
@Override
public boolean apply(VolumeApi volumeApi) {
return volumeApi.get(testVolume.getId()) == null;
}
}, 30 * 1000L).apply(volumeApi));
}
super.tearDownContext();
}
public void testCreateVolume() {
CreateVolumeOptions options = CreateVolumeOptions.Builder
.name(name)
.description("description of test volume")
.availabilityZone(zone);
testVolume = volumeApi.create(100, options);
assertTrue(new RetryablePredicate<VolumeApi>(new Predicate<VolumeApi>() {
@Override
public boolean apply(VolumeApi volumeApi) {
return volumeApi.get(testVolume.getId()).getStatus() == Volume.Status.AVAILABLE;
}
}, 600 * 1000L).apply(volumeApi));
}
@Test(dependsOnMethods = "testCreateVolume")
public void testListVolumes() {
Set<? extends Volume> volumes = volumeApi.list().toImmutableSet();
assertNotNull(volumes);
boolean foundIt = false;
for (Volume vol : volumes) {
Volume details = volumeApi.get(vol.getId());
assertNotNull(details);
if (Objects.equal(details.getId(), testVolume.getId())) {
foundIt = true;
}
}
assertTrue(foundIt, "Failed to find the volume we created in list() response");
}
@Test(dependsOnMethods = "testCreateVolume")
public void testListVolumesInDetail() {
Set<? extends Volume> volumes = volumeApi.listInDetail().toImmutableSet();
assertNotNull(volumes);
boolean foundIt = false;
for (Volume vol : volumes) {
Volume details = volumeApi.get(vol.getId());
assertNotNull(details);
assertNotNull(details.getId());
assertNotNull(details.getCreated());
assertTrue(details.getSize() > -1);
assertEquals(details.getId(), vol.getId());
assertEquals(details.getSize(), vol.getSize());
assertEquals(details.getName(), vol.getName());
assertEquals(details.getDescription(), vol.getDescription());
assertEquals(details.getCreated(), vol.getCreated());
if (Objects.equal(details.getId(), testVolume.getId())) {
foundIt = true;
}
}
assertTrue(foundIt, "Failed to find the volume we previously created in listInDetail() response");
}
@Test(dependsOnMethods = "testCreateVolume")
public void testCreateSnapshot() {
testSnapshot = snapshotApi.create(
testVolume.getId(),
CreateSnapshotOptions.Builder.name("jclouds-live-test").description(
"jclouds live test snapshot").force());
assertNotNull(testSnapshot);
assertNotNull(testSnapshot.getId());
final String snapshotId = testSnapshot.getId();
assertNotNull(testSnapshot.getStatus());
assertTrue(testSnapshot.getSize() > -1);
assertNotNull(testSnapshot.getCreated());
assertTrue(new RetryablePredicate<VolumeApi>(new Predicate<VolumeApi>() {
@Override
public boolean apply(VolumeApi volumeApi) {
return snapshotApi.get(snapshotId).getStatus() == Volume.Status.AVAILABLE;
}
}, 1200 * 1000L).apply(volumeApi));
}
@Test(dependsOnMethods = "testCreateSnapshot")
public void testListSnapshots() {
Set<? extends Snapshot> snapshots = snapshotApi.list().toImmutableSet();
assertNotNull(snapshots);
boolean foundIt = false;
for (Snapshot snap : snapshots) {
Snapshot details = snapshotApi.get(snap.getId());
if (Objects.equal(snap.getVolumeId(), testVolume.getId())) {
foundIt = true;
}
assertNotNull(details);
assertEquals(details.getId(), snap.getId());
assertEquals(details.getVolumeId(), snap.getVolumeId());
}
assertTrue(foundIt, "Failed to find the snapshot we previously created in listSnapshots() response");
}
@Test(dependsOnMethods = "testCreateSnapshot")
public void testListSnapshotsInDetail() {
Set<? extends Snapshot> snapshots = snapshotApi.listInDetail().toImmutableSet();
assertNotNull(snapshots);
boolean foundIt = false;
for (Snapshot snap : snapshots) {
Snapshot details = snapshotApi.get(snap.getId());
if (Objects.equal(snap.getVolumeId(), testVolume.getId())) {
foundIt = true;
assertSame(details, testSnapshot);
}
assertSame(details, snap);
}
assertTrue(foundIt, "Failed to find the snapshot we created in listSnapshotsInDetail() response");
}
private void assertSame(Snapshot a, Snapshot b) {
assertNotNull(a);
assertNotNull(b);
assertEquals(a.getId(), b.getId());
assertEquals(a.getDescription(), b.getDescription());
assertEquals(a.getName(), b.getName());
assertEquals(a.getVolumeId(), b.getVolumeId());
}
}

View File

@ -0,0 +1,234 @@
/**
* 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.cinder.v1.features;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import java.net.URI;
import java.util.Set;
import javax.ws.rs.core.MediaType;
import org.jclouds.date.DateService;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.http.HttpResponse;
import org.jclouds.openstack.cinder.v1.domain.Volume;
import org.jclouds.openstack.cinder.v1.domain.VolumeAttachment;
import org.jclouds.openstack.cinder.v1.internal.BaseCinderApiExpectTest;
import org.jclouds.openstack.cinder.v1.options.CreateVolumeOptions;
import org.jclouds.rest.ResourceNotFoundException;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
/**
* Tests VolumeApi Guice wiring and parsing
*
* @author Everett Toews
*/
@Test(groups = "unit", testName = "VolumeApiExpectTest")
public class VolumeApiExpectTest extends BaseCinderApiExpectTest {
private DateService dateService = new SimpleDateFormatDateService();
public void testListVolumes() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/volumes");
VolumeApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).build(),
HttpResponse.builder().statusCode(200).payload(payloadFromResource("/volume_list_simple.json")).build()
).getVolumeApiForZone("RegionOne");
Set<? extends Volume> volumes = api.list().toImmutableSet();
assertEquals(volumes, ImmutableSet.of(testVolume()));
}
public void testListVolumesFail() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/volumes");
VolumeApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).build(),
HttpResponse.builder().statusCode(404).build()
).getVolumeApiForZone("RegionOne");
Set<? extends Volume> volumes = api.list().toImmutableSet();
assertTrue(volumes.isEmpty());
}
public void testListVolumesInDetail() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/volumes/detail");
VolumeApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).build(),
HttpResponse.builder().statusCode(200).payload(payloadFromResource("/volume_list_details.json")).build()
).getVolumeApiForZone("RegionOne");
Set<? extends Volume> volumes = api.listInDetail().toImmutableSet();
assertEquals(volumes, ImmutableSet.of(testVolume()));
}
public void testListVolumesInDetailFail() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/volumes/detail");
VolumeApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).build(),
HttpResponse.builder().statusCode(404).build()
).getVolumeApiForZone("RegionOne");
Set<? extends Volume> volumes = api.listInDetail().toImmutableSet();
assertTrue(volumes.isEmpty());
}
public void testCreateVolume() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/volumes");
VolumeApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint)
.method("POST")
.payload(payloadFromResourceWithContentType("/volume_create.json", MediaType.APPLICATION_JSON))
.build(),
HttpResponse.builder().statusCode(200).payload(payloadFromResource("/volume_create_response.json")).build()
).getVolumeApiForZone("RegionOne");
CreateVolumeOptions options = CreateVolumeOptions.Builder
.name("jclouds-test-volume")
.description("description of test volume");
Volume volume = api.create(1, options);
assertEquals(volume, testVolumeCreate());
}
@Test(expectedExceptions = ResourceNotFoundException.class)
public void testCreateVolumeFail() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/volumes");
VolumeApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint)
.endpoint(endpoint)
.method("POST")
.payload(payloadFromResourceWithContentType("/volume_create.json", MediaType.APPLICATION_JSON))
.build(),
HttpResponse.builder().statusCode(404).payload(payloadFromResource("/volume_create_response.json")).build()
).getVolumeApiForZone("RegionOne");
CreateVolumeOptions options = CreateVolumeOptions.Builder
.name("jclouds-test-volume")
.description("description of test volume");
api.create(1, options);
}
public void testGetVolume() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/volumes/60761c60-0f56-4499-b522-ff13e120af10");
VolumeApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).build(),
HttpResponse.builder().statusCode(200).payload(payloadFromResource("/volume_get.json")).build()
).getVolumeApiForZone("RegionOne");
Volume volume = api.get("60761c60-0f56-4499-b522-ff13e120af10");
assertEquals(volume, testVolume());
// double-check equals()
assertEquals(volume.getName(), "test");
assertEquals(volume.getZone(), "nova");
assertEquals(volume.getStatus(), Volume.Status.IN_USE);
assertEquals(volume.getDescription(), "This is a test volume");
assertEquals(Iterables.getOnlyElement(volume.getAttachments()), testAttachment());
}
public void testGetVolumeFail() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/volumes/60761c60-0f56-4499-b522-ff13e120af10");
VolumeApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).build(),
HttpResponse.builder().statusCode(404).build()
).getVolumeApiForZone("RegionOne");
assertNull(api.get("60761c60-0f56-4499-b522-ff13e120af10"));
}
public void testDeleteVolume() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/volumes/60761c60-0f56-4499-b522-ff13e120af10");
VolumeApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).method("DELETE").build(),
HttpResponse.builder().statusCode(202).build()
).getVolumeApiForZone("RegionOne");
assertTrue(api.delete("60761c60-0f56-4499-b522-ff13e120af10"));
}
public void testDeleteVolumeFail() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/volumes/60761c60-0f56-4499-b522-ff13e120af10");
VolumeApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).method("DELETE").build(),
HttpResponse.builder().statusCode(404).build()
).getVolumeApiForZone("RegionOne");
assertFalse(api.delete("60761c60-0f56-4499-b522-ff13e120af10"));
}
protected Volume testVolumeCreate() {
return Volume.builder()
.id("60761c60-0f56-4499-b522-ff13e120af10")
.size(1)
.name("jclouds-test-volume")
.zone("nova")
.status(Volume.Status.CREATING)
.volumeType("None")
.description("description of test volume")
.created(dateService.iso8601DateParse("2012-10-29T20:53:28.000000"))
.build();
}
protected Volume testVolume() {
return Volume.builder()
.id("60761c60-0f56-4499-b522-ff13e120af10")
.size(1)
.name("test")
.zone("nova")
.status(Volume.Status.IN_USE)
.volumeType("None")
.description("This is a test volume")
.attachments(ImmutableSet.of(testAttachment()))
.created(dateService.iso8601DateParse("2012-10-29T20:53:28.000000"))
.build();
}
protected VolumeAttachment testAttachment() {
return VolumeAttachment.builder()
.id("60761c60-0f56-4499-b522-ff13e120af10")
.volumeId("60761c60-0f56-4499-b522-ff13e120af10")
.serverId("0229a1c1-d54a-4836-8527-2ab28b42e2bb")
.device("/dev/vdc")
.build();
}
}

View File

@ -0,0 +1,92 @@
/**
* 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.cinder.v1.features;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import java.net.URI;
import java.util.Set;
import org.jclouds.date.DateService;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.http.HttpResponse;
import org.jclouds.openstack.cinder.v1.domain.VolumeType;
import org.jclouds.openstack.cinder.v1.internal.BaseCinderApiExpectTest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
/**
* Tests Guice wiring and parsing of VolumeTypeApi
*
* @author Everett Toews
*/
@Test(groups = "unit", testName = "VolumeTypeApiExpectTest")
public class VolumeTypeApiExpectTest extends BaseCinderApiExpectTest {
private DateService dateService = new SimpleDateFormatDateService();
public void testListVolumeTypes() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/types");
VolumeTypeApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).build(),
HttpResponse.builder().statusCode(200).payload(payloadFromResource("/volume_type_list_simple.json")).build()
).getVolumeTypeApiForZone("RegionOne");
Set<? extends VolumeType> types = api.list().toImmutableSet();
assertEquals(types, ImmutableSet.of(testVolumeType()));
}
public void testGetVolumeType() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/types/1");
VolumeTypeApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).build(),
HttpResponse.builder().statusCode(200).payload(payloadFromResource("/volume_type_get.json")).build()
).getVolumeTypeApiForZone("RegionOne");
VolumeType type = api.get("1");
assertEquals(type, testVolumeType());
}
public void testGetVolumeTypeFailNotFound() {
URI endpoint = URI.create("http://172.16.0.1:8776/v1/50cdb4c60374463198695d9f798fa34d/types/X");
VolumeTypeApi api = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess,
authenticatedGET().endpoint(endpoint).build(),
HttpResponse.builder().statusCode(404).build()
).getVolumeTypeApiForZone("RegionOne");
assertNull(api.get("X"));
}
public VolumeType testVolumeType() {
return VolumeType.builder()
.id("1")
.name("jclouds-test-1")
.created(dateService.iso8601SecondsDateParse("2012-05-10 12:33:06"))
.extraSpecs(ImmutableMap.of("test", "value1", "test1", "wibble"))
.build();
}
}

View File

@ -0,0 +1,66 @@
/**
* 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.cinder.v1.features;
import static org.testng.Assert.assertNotNull;
import java.util.Set;
import org.jclouds.openstack.cinder.v1.domain.VolumeType;
import org.jclouds.openstack.cinder.v1.internal.BaseCinderApiLiveTest;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test;
import com.google.common.collect.Iterables;
/**
* Tests behavior of VolumeTypeApi.
*
* @author Everett Toews
*/
@Test(groups = "live", testName = "VolumeTypeApiLiveTest", singleThreaded = true)
public class VolumeTypeApiLiveTest extends BaseCinderApiLiveTest {
private VolumeTypeApi volumeTypeApi;
private String zone;
@BeforeGroups(groups = {"integration", "live"})
@Override
public void setupContext() {
super.setupContext();
zone = Iterables.getLast(cinder.getApi().getConfiguredZones(), "nova");
volumeTypeApi = cinder.getApi().getVolumeTypeApiForZone(zone);
}
@AfterClass(groups = { "integration", "live" })
@Override
protected void tearDownContext() {
super.tearDownContext();
}
public void testListAndGetVolumeTypes() {
Set<? extends VolumeType> volumeTypes = volumeTypeApi.list().toImmutableSet();
assertNotNull(volumeTypes);
for (VolumeType vt : volumeTypes) {
VolumeType details = volumeTypeApi.get(vt.getId());
assertNotNull(details);
}
}
}

View File

@ -0,0 +1,29 @@
/**
* 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.cinder.v1.internal;
import org.jclouds.openstack.cinder.v1.CinderApi;
/**
* Base class for writing Volume Rest Api Expect tests
*
* @author Everett Toews
*/
public class BaseCinderApiExpectTest extends BaseCinderExpectTest<CinderApi> {
}

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.cinder.v1.internal;
import java.util.Properties;
import org.jclouds.apis.BaseContextLiveTest;
import org.jclouds.openstack.cinder.v1.CinderApi;
import org.jclouds.openstack.cinder.v1.CinderApiMetadata;
import org.jclouds.openstack.cinder.v1.CinderAsyncApi;
import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties;
import org.jclouds.rest.RestContext;
import org.testng.annotations.BeforeGroups;
import com.google.common.reflect.TypeToken;
/**
* Tests behavior of CinderApi
*
* @author Everett Toews
*/
public class BaseCinderApiLiveTest extends BaseContextLiveTest<RestContext<CinderApi, CinderAsyncApi>> {
public BaseCinderApiLiveTest() {
provider = "openstack-cinder";
}
protected RestContext<CinderApi, CinderAsyncApi> cinder;
@BeforeGroups(groups = { "integration", "live" })
@Override
public void setupContext() {
super.setupContext();
cinder = context;
}
@Override
protected Properties setupProperties() {
Properties props = super.setupProperties();
setIfTestSystemPropertyPresent(props, KeystoneProperties.CREDENTIAL_TYPE);
return props;
}
@Override
protected TypeToken<RestContext<CinderApi, CinderAsyncApi>> contextType() {
return CinderApiMetadata.CONTEXT_TOKEN;
}
}

View File

@ -0,0 +1,74 @@
/**
* 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.cinder.v1.internal;
import javax.ws.rs.core.MediaType;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.openstack.keystone.v2_0.internal.KeystoneFixture;
import org.jclouds.rest.internal.BaseRestApiExpectTest;
/**
* Base class for writing Nova Expect tests
*
* @author Adrian Cole
*/
public class BaseCinderExpectTest<T> extends BaseRestApiExpectTest<T> {
protected HttpRequest keystoneAuthWithUsernameAndPassword;
protected HttpRequest keystoneAuthWithUsernameAndPasswordAndTenantName;
protected HttpRequest keystoneAuthWithAccessKeyAndSecretKeyAndTenantName;
protected String authToken;
protected HttpResponse responseWithKeystoneAccess;
protected HttpRequest extensionsOfNovaRequest;
protected HttpResponse extensionsOfNovaResponse;
protected HttpResponse unmatchedExtensionsOfNovaResponse;
protected HttpRequest keystoneAuthWithAccessKeyAndSecretKeyAndTenantId;
protected String identityWithTenantId;
public BaseCinderExpectTest() {
provider = "openstack-cinder";
keystoneAuthWithUsernameAndPassword = KeystoneFixture.INSTANCE.initialAuthWithUsernameAndPassword(identity,
credential);
keystoneAuthWithUsernameAndPasswordAndTenantName = KeystoneFixture.INSTANCE.initialAuthWithUsernameAndPasswordAndTenantName(identity,
credential);
keystoneAuthWithAccessKeyAndSecretKeyAndTenantName = KeystoneFixture.INSTANCE.initialAuthWithAccessKeyAndSecretKeyAndTenantName(identity,
credential);
keystoneAuthWithAccessKeyAndSecretKeyAndTenantId = KeystoneFixture.INSTANCE.initialAuthWithAccessKeyAndSecretKeyAndTenantId(identity,
credential);
authToken = KeystoneFixture.INSTANCE.getAuthToken();
responseWithKeystoneAccess = KeystoneFixture.INSTANCE.responseWithAccess();
// now, createContext arg will need tenant prefix
identityWithTenantId = KeystoneFixture.INSTANCE.getTenantId() + ":" + identity;
identity = KeystoneFixture.INSTANCE.getTenantName() + ":" + identity;
}
@Override
protected HttpRequestComparisonType compareHttpRequestAsType(HttpRequest input) {
return HttpRequestComparisonType.JSON;
}
protected HttpRequest.Builder<?> authenticatedGET() {
return HttpRequest.builder()
.method("GET")
.addHeader("Accept", MediaType.APPLICATION_JSON)
.addHeader("X-Auth-Token", authToken);
}
}

View File

@ -0,0 +1 @@
{"snapshot":{"display_name":"jclouds-test-snapshot","volume_id":"ea6f70ef-2784-40b9-9d14-d7f33c507c3f","display_description":"jclouds test snapshot","force":"true"}}

View File

@ -0,0 +1,11 @@
{
"snapshot": {
"status": "creating",
"display_name": "jclouds-test-snapshot",
"created_at": "2012-11-02T16:23:27.000000",
"display_description": "jclouds test snapshot",
"volume_id": "ea6f70ef-2784-40b9-9d14-d7f33c507c3f",
"id": "67d03df1-ce5d-4ba7-adbe-492ceb80170b",
"size": 1
}
}

View File

@ -0,0 +1,13 @@
{
"snapshot": {
"status": "available",
"display_name": "jclouds-test-snapshot",
"created_at": "2012-11-02T16:23:27.000000",
"display_description": "jclouds test snapshot",
"os-extended-snapshot-attributes:progress": "100%",
"volume_id": "ea6f70ef-2784-40b9-9d14-d7f33c507c3f",
"os-extended-snapshot-attributes:project_id": "cc03fd4f503f4d9c986b381b8abe6af5",
"id": "67d03df1-ce5d-4ba7-adbe-492ceb80170b",
"size": 1
}
}

View File

@ -0,0 +1,15 @@
{
"snapshots": [
{
"status": "available",
"display_name": "jclouds-test-snapshot",
"created_at": "2012-11-02T16:23:27.000000",
"display_description": "jclouds test snapshot",
"os-extended-snapshot-attributes:progress": "100%",
"volume_id": "ea6f70ef-2784-40b9-9d14-d7f33c507c3f",
"os-extended-snapshot-attributes:project_id": "cc03fd4f503f4d9c986b381b8abe6af5",
"id": "67d03df1-ce5d-4ba7-adbe-492ceb80170b",
"size": 1
}
]
}

View File

@ -0,0 +1,13 @@
{
"snapshots": [
{
"status": "available",
"display_name": "jclouds-test-snapshot",
"created_at": "2012-11-02T16:23:27.000000",
"display_description": "jclouds test snapshot",
"volume_id": "ea6f70ef-2784-40b9-9d14-d7f33c507c3f",
"id": "67d03df1-ce5d-4ba7-adbe-492ceb80170b",
"size": 1
}
]
}

View File

@ -0,0 +1 @@
{"volume":{"display_name":"jclouds-test-volume","display_description":"description of test volume","size":1}}

View File

@ -0,0 +1,15 @@
{
"volume": {
"status": "creating",
"display_name": "jclouds-test-volume",
"attachments": [],
"availability_zone": "nova",
"created_at": "2012-10-29T20:53:28.000000",
"display_description": "description of test volume",
"volume_type": "None",
"snapshot_id": null,
"metadata": {},
"id": "60761c60-0f56-4499-b522-ff13e120af10",
"size": 1
}
}

View File

@ -0,0 +1,22 @@
{
"volume": {
"status": "in-use",
"display_name": "test",
"attachments": [
{
"device": "/dev/vdc",
"server_id": "0229a1c1-d54a-4836-8527-2ab28b42e2bb",
"id": "60761c60-0f56-4499-b522-ff13e120af10",
"volume_id": "60761c60-0f56-4499-b522-ff13e120af10"
}
],
"availability_zone": "nova",
"created_at": "2012-10-29T20:53:28.000000",
"display_description": "This is a test volume",
"volume_type": "None",
"snapshot_id": null,
"metadata": {},
"id": "60761c60-0f56-4499-b522-ff13e120af10",
"size": 1
}
}

View File

@ -0,0 +1,24 @@
{
"volumes": [
{
"status": "in-use",
"display_name": "test",
"attachments": [
{
"device": "/dev/vdc",
"server_id": "0229a1c1-d54a-4836-8527-2ab28b42e2bb",
"id": "60761c60-0f56-4499-b522-ff13e120af10",
"volume_id": "60761c60-0f56-4499-b522-ff13e120af10"
}
],
"availability_zone": "nova",
"created_at": "2012-10-29T20:53:28.000000",
"display_description": "This is a test volume",
"volume_type": "None",
"snapshot_id": null,
"metadata": {},
"id": "60761c60-0f56-4499-b522-ff13e120af10",
"size": 1
}
]
}

View File

@ -0,0 +1,24 @@
{
"volumes": [
{
"status": "in-use",
"display_name": "test",
"attachments": [
{
"device": "/dev/vdc",
"server_id": "0229a1c1-d54a-4836-8527-2ab28b42e2bb",
"id": "60761c60-0f56-4499-b522-ff13e120af10",
"volume_id": "60761c60-0f56-4499-b522-ff13e120af10"
}
],
"availability_zone": "nova",
"created_at": "2012-10-29T20:53:28.000000",
"display_description": "This is a test volume",
"volume_type": "None",
"snapshot_id": null,
"metadata": {},
"id": "60761c60-0f56-4499-b522-ff13e120af10",
"size": 1
}
]
}

View File

@ -0,0 +1,14 @@
{
"volume_type": {
"name": "jclouds-test-1",
"deleted": false,
"created_at": "2012-05-10 12:33:06",
"updated_at": null,
"extra_specs": {
"test": "value1",
"test1": "wibble"
},
"deleted_at": null,
"id": 1
}
}

View File

@ -0,0 +1,16 @@
{
"volume_types": [
{
"name": "jclouds-test-1",
"deleted": false,
"created_at": "2012-05-10 12:33:06",
"updated_at": null,
"extra_specs": {
"test": "value1",
"test1": "wibble"
},
"deleted_at": null,
"id": 1
}
]
}

View File

@ -18,7 +18,8 @@
specific language governing permissions and limitations specific language governing permissions and limitations
under the License. under the License.
--><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<artifactId>jclouds-project</artifactId> <artifactId>jclouds-project</artifactId>
@ -61,5 +62,6 @@
<module>fgcp-de</module> <module>fgcp-de</module>
<module>abiquo</module> <module>abiquo</module>
<module>oauth</module> <module>oauth</module>
<module>openstack-cinder</module>
</modules> </modules>
</project> </project>