Merge pull request #581 from aplowe/openstack-nova

Openstack nova: adding Volumes extension
This commit is contained in:
Adrian Cole 2012-04-24 11:53:48 -07:00
commit b8b1a5f58a
22 changed files with 2237 additions and 0 deletions

View File

@ -28,6 +28,7 @@ import org.jclouds.openstack.nova.v1_1.extensions.HostAdministrationAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.KeyPairAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.SimpleTenantUsageAsyncClient;
import org.jclouds.openstack.nova.v1_1.extensions.VolumeAsyncClient;
import org.jclouds.openstack.nova.v1_1.features.ExtensionAsyncClient;
import org.jclouds.openstack.nova.v1_1.features.FlavorAsyncClient;
import org.jclouds.openstack.nova.v1_1.features.ImageAsyncClient;
@ -120,4 +121,10 @@ public interface NovaAsyncClient {
Optional<SimpleTenantUsageAsyncClient> getSimpleTenantUsageExtensionForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
/**
* Provides asynchronous access to Volume features.
*/
@Delegate
Optional<VolumeAsyncClient> getVolumeExtensionForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
}

View File

@ -30,6 +30,7 @@ import org.jclouds.openstack.nova.v1_1.extensions.HostAdministrationClient;
import org.jclouds.openstack.nova.v1_1.extensions.KeyPairClient;
import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupClient;
import org.jclouds.openstack.nova.v1_1.extensions.SimpleTenantUsageClient;
import org.jclouds.openstack.nova.v1_1.extensions.VolumeClient;
import org.jclouds.openstack.nova.v1_1.features.ExtensionClient;
import org.jclouds.openstack.nova.v1_1.features.FlavorClient;
import org.jclouds.openstack.nova.v1_1.features.ImageClient;
@ -122,4 +123,12 @@ public interface NovaClient {
Optional<SimpleTenantUsageClient> getSimpleTenantUsageExtensionForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
/**
* Provides synchronous access to Volume features.
*/
@Delegate
Optional<VolumeClient> getVolumeExtensionForZone(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
}

View File

@ -73,6 +73,7 @@ public class NovaRestClientModule extends RestClientModule<NovaClient, NovaAsync
.put(KeyPairClient.class, KeyPairAsyncClient.class)
.put(HostAdministrationClient.class, HostAdministrationAsyncClient.class)
.put(SimpleTenantUsageClient.class, SimpleTenantUsageAsyncClient.class)
.put(VolumeClient.class, VolumeAsyncClient.class)
.build();
public NovaRestClientModule() {

View File

@ -0,0 +1,332 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.nova.v1_1.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.Set;
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;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.gson.annotations.SerializedName;
/**
* An Openstack Nova Volume
*/
public class Volume {
public static enum Status {
CREATING, AVAILABLE, IN_USE, DELETING, ERROR, 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();
private String id;
private Status status;
private int size;
private String zone;
private Date created;
private Set<VolumeAttachment> attachments = Sets.newLinkedHashSet();
private String volumeType;
private String snapshotId;
private String name;
private String description;
private Map<String, String> metadata = Maps.newHashMap();
/** @see Volume#getId() */
public T id(String id) {
this.id = id;
return self();
}
/** @see Volume#getStatus() */
public T status(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 = attachments;
return self();
}
/** @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#getMetadata() */
public T metadata(Map<String, String> metadata) {
this.metadata = metadata;
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();
}
public Volume build() {
return new Volume(this);
}
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())
.metadata(in.getMetadata())
;
}
}
private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@Override
protected ConcreteBuilder self() {
return this;
}
}
private final String id;
private final Status status;
private final int size;
@SerializedName(value="availabilityZone")
private final String zone;
@SerializedName(value="createdAt")
private final Date created;
private final Set<VolumeAttachment> attachments;
private final String volumeType;
private final String snapshotId;
@SerializedName(value="displayName")
private final String name;
@SerializedName(value="displayDescription")
private final String description;
private final Map<String, String> metadata;
protected Volume(Builder<?> builder) {
this.id = builder.id;
this.status = builder.status;
this.size = builder.size;
this.zone = builder.zone;
this.created = builder.created;
this.attachments = ImmutableSet.copyOf(checkNotNull(builder.attachments, "attachments"));
this.volumeType = builder.volumeType;
this.snapshotId = builder.snapshotId;
this.name = builder.name;
this.description = builder.description;
this.metadata = ImmutableMap.copyOf(checkNotNull(builder.metadata, "metadata"));
}
/**
* @return the id of this volume
*/
public String getId() {
return this.id;
}
/**
* @return the status of this volume
*/
public 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)
*/
@Nullable
public Set<VolumeAttachment> getAttachments() {
return Collections.unmodifiableSet(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;
}
@Nullable
public Map<String, String> getMetadata() {
return Collections.unmodifiableMap(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("")
.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,168 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.nova.v1_1.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
/**
* An Openstack Nova 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().fromAttachment(this);
}
public static abstract class Builder<T extends Builder<T>> {
protected abstract T self();
private String id;
private String volumeId;
private String serverId;
private 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(this);
}
public T fromAttachment(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;
private final String volumeId;
private final String serverId;
private final String device;
protected VolumeAttachment(Builder<?> builder) {
this.id = checkNotNull(builder.id, "id");
this.volumeId = checkNotNull(builder.volumeId, "volumeId");
this.serverId = builder.serverId;
this.device = builder.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("")
.add("id", id)
.add("volumeId", volumeId)
.add("serverId", serverId)
.add("device", device)
;
}
@Override
public String toString() {
return string().toString();
}
}

View File

@ -0,0 +1,233 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.nova.v1_1.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Date;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.gson.annotations.SerializedName;
/**
* An Openstack Nova Volume Snapshot
*/
public class VolumeSnapshot {
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();
private String id;
private String volumeId;
private Volume.Status status;
private int size;
private Date created;
private String name;
private String description;
/** @see VolumeSnapshot#getId() */
public T id(String id) {
this.id = id;
return self();
}
/** @see VolumeSnapshot#getVolumeId() */
public T volumeId(String volumeId) {
this.volumeId = volumeId;
return self();
}
/** @see VolumeSnapshot#getStatus() */
public T status(Volume.Status status) {
this.status = status;
return self();
}
/** @see VolumeSnapshot#getSize() */
public T size(int size) {
this.size = size;
return self();
}
/** @see VolumeSnapshot#getCreated() */
public T created(Date created) {
this.created = created;
return self();
}
/** @see VolumeSnapshot#getName() */
public T name(String name) {
this.name = name;
return self();
}
/** @see VolumeSnapshot#getDescription() */
public T description(String description) {
this.description = description;
return self();
}
public VolumeSnapshot build() {
return new VolumeSnapshot(this);
}
public T fromSnapshot(VolumeSnapshot 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;
private final String volumeId;
private final Volume.Status status;
private final int size;
@SerializedName(value="createdAt")
private final Date created;
@SerializedName(value="displayName")
private final String name;
@SerializedName(value="displayDescription")
private final String description;
protected VolumeSnapshot(Builder<?> builder) {
this.id = checkNotNull(builder.id, "id");
this.volumeId = checkNotNull(builder.volumeId, "volumeId");
this.status = checkNotNull(builder.status, "status");
this.size = builder.size;
this.created = builder.created;
this.name = builder.name;
this.description = builder.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;
VolumeSnapshot that = VolumeSnapshot.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("")
.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,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.nova.v1_1.extensions;
import java.util.Set;
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.filters.AuthenticateRequest;
import org.jclouds.openstack.nova.v1_1.domain.VolumeAttachment;
import org.jclouds.openstack.nova.v1_1.domain.VolumeSnapshot;
import org.jclouds.openstack.nova.v1_1.domain.Volume;
import org.jclouds.openstack.nova.v1_1.options.CreateVolumeSnapshotOptions;
import org.jclouds.openstack.nova.v1_1.options.CreateVolumeOptions;
import org.jclouds.openstack.services.Extension;
import org.jclouds.openstack.services.ServiceType;
import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.MapBinder;
import org.jclouds.rest.annotations.Payload;
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.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import com.google.common.util.concurrent.ListenableFuture;
/**
* Provides synchronous access to Volumes.
* <p/>
*
* @see org.jclouds.openstack.nova.v1_1.extensions.VolumeAsyncClient
* @author Adam Lowe
*/
@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.VOLUMES)
@SkipEncoding({'/', '='})
@RequestFilters(AuthenticateRequest.class)
public interface VolumeAsyncClient {
/**
* Returns a summary list of volumes.
*
* @return the list of volumes
*/
@GET
@Path("/os-volumes")
@SelectJson("volumes")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<Set<Volume>> listVolumes();
/**
* Returns a detailed list of volumes.
*
* @return the list of volumes.
*/
@GET
@Path("/os-volumes/detail")
@SelectJson("volumes")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<Set<Volume>> listVolumesInDetail();
/**
* Return data about the given volume.
*
* @return details of a specific volume.
*/
@GET
@Path("/os-volumes/{id}")
@SelectJson("volume")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<Volume> getVolume(@PathParam("id") String volumeId);
/**
* Creates a new volume
*
* @return the new Snapshot
*/
@POST
@Path("/os-volumes")
@SelectJson("volume")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@MapBinder(CreateVolumeOptions.class)
ListenableFuture<Volume> createVolume(@PayloadParam("size") int sizeGB, CreateVolumeOptions... options);
/**
* Delete a volume.
*
* @return true if successful
*/
@DELETE
@Path("/os-volumes/{id}")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
ListenableFuture<Boolean> deleteVolume(@PathParam("id") String volumeId);
/**
* List volume attachments for a given instance.
*
* @return all Floating IPs
*/
@GET
@Path("/servers/{server_id}/os-volume_attachments")
@SelectJson("volumeAttachments")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<Set<VolumeAttachment>> listAttachmentsOnServer(@PathParam("server_id") String serverId);
/**
* Get a specific attached volume.
*
* @return data about the given volume attachment.
*/
@GET
@Path("/servers/{server_id}/os-volume_attachments/{id}")
@SelectJson("volumeAttachment")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<VolumeAttachment> getAttachmentForVolumeOnServer(@PathParam("id") String volumeId,
@PathParam("server_id") String serverId);
/**
* Attach a volume to an instance
*
* @return the new Attachment
*/
@POST
@Path("/servers/{server_id}/os-volume_attachments")
@SelectJson("volumeAttachment")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Payload("%7B\"volumeAttachment\":%7B\"volumeId\":\"{id}\",\"device\":\"{device}\"%7D%7D")
ListenableFuture<VolumeAttachment> attachVolumeToServerAsDevice(@PayloadParam("id") String volumeId,
@PathParam("server_id") String serverId, @PayloadParam("device") String device);
/**
* Detach a Volume from an instance.
*
* @return true if successful
*/
@DELETE
@Path("/servers/{server_id}/os-volume_attachments/{id}")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
ListenableFuture<Boolean> detachVolumeFromServer(@PathParam("id") String volumeId, @PathParam("server_id") String serverId);
/**
* Returns a summary list of snapshots.
*
* @return the list of snapshots
*/
@GET
@Path("/os-snapshots")
@SelectJson("snapshots")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<Set<VolumeSnapshot>> listSnapshots();
/**
* Returns a summary list of snapshots.
*
* @return the list of snapshots
*/
@GET
@Path("/os-snapshots/detail")
@SelectJson("snapshots")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<Set<VolumeSnapshot>> listSnapshotsInDetail();
/**
* Return data about the given snapshot.
*
* @return details of a specific snapshot.
*/
@GET
@Path("/os-snapshots/{id}")
@SelectJson("snapshot")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<VolumeSnapshot> getSnapshot(@PathParam("id") String snapshotId);
/**
* Creates a new Snapshot
*
* @return the new Snapshot
*/
@POST
@Path("/os-snapshots")
@SelectJson("snapshot")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@MapBinder(CreateVolumeSnapshotOptions.class)
ListenableFuture<VolumeSnapshot> createSnapshot(@PayloadParam("volume_id") String volumeId, CreateVolumeSnapshotOptions... options);
/**
* Delete a snapshot.
*
* @return true if successful
*/
@DELETE
@Path("/os-snapshots/{id}")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
ListenableFuture<Boolean> deleteSnapshot(@PathParam("id") String snapshotId);
}

View File

@ -0,0 +1,141 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.nova.v1_1.extensions;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.jclouds.concurrent.Timeout;
import org.jclouds.openstack.nova.v1_1.domain.VolumeAttachment;
import org.jclouds.openstack.nova.v1_1.domain.VolumeSnapshot;
import org.jclouds.openstack.nova.v1_1.domain.Volume;
import org.jclouds.openstack.nova.v1_1.options.CreateVolumeSnapshotOptions;
import org.jclouds.openstack.nova.v1_1.options.CreateVolumeOptions;
import org.jclouds.openstack.services.Extension;
import org.jclouds.openstack.services.ServiceType;
/**
* Provides synchronous access to Volumes.
* <p/>
*
* @see org.jclouds.openstack.nova.v1_1.extensions.VolumeAsyncClient
* @author Adam Lowe
*/
@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.VOLUMES)
@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS)
public interface VolumeClient {
/**
* Returns a summary list of snapshots.
*
* @return the list of snapshots
*/
Set<Volume> listVolumes();
/**
* Returns a detailed list of volumes.
*
* @return the list of volumes.
*/
Set<Volume> listVolumesInDetail();
/**
* Return data about the given volume.
*
* @return details of a specific snapshot.
*/
Volume getVolume(String volumeId);
/**
* Creates a new Snapshot
*
* @return the new Snapshot
*/
Volume createVolume(int sizeGB, CreateVolumeOptions... options);
/**
* Delete a snapshot.
*
* @return true if successful
*/
Boolean deleteVolume(String volumeId);
/**
* List volume attachments for a given instance.
*
* @return all Floating IPs
*/
Set<VolumeAttachment> listAttachmentsOnServer(String serverId);
/**
* Get a specific attached volume.
*
* @return data about the given volume attachment.
*/
VolumeAttachment getAttachmentForVolumeOnServer(String volumeId, String serverId);
/**
* Attach a volume to an instance
*
* @return data about the new volume attachment
*/
VolumeAttachment attachVolumeToServerAsDevice(String volumeId, String serverId, String device);
/**
* Detach a Volume from an instance.
*
* @return true if successful
*/
Boolean detachVolumeFromServer(String server_id, String volumeId);
/**
* Returns a summary list of snapshots.
*
* @return the list of snapshots
*/
Set<VolumeSnapshot> listSnapshots();
/**
* Returns a summary list of snapshots.
*
* @return the list of snapshots
*/
Set<VolumeSnapshot> listSnapshotsInDetail();
/**
* Return data about the given snapshot.
*
* @return details of a specific snapshot.
*/
VolumeSnapshot getSnapshot(String snapshotId);
/**
* Creates a new Snapshot
*
* @return the new Snapshot
*/
VolumeSnapshot createSnapshot(String volumeId, CreateVolumeSnapshotOptions... options);
/**
* Delete a snapshot.
*
* @return true if successful
*/
Boolean deleteSnapshot(String snapshotId);
}

View File

@ -66,6 +66,8 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio
URI.create("http://docs.openstack.org/compute/ext/os-simple-tenant-usage/api/v1.1"))
.put(URI.create(ExtensionNamespaces.HOSTS),
URI.create("http://docs.openstack.org/compute/ext/hosts/api/v1.1"))
.put(URI.create(ExtensionNamespaces.VOLUMES),
URI.create("http://docs.openstack.org/compute/ext/volumes/api/v1.1"))
.build();
@Inject

View File

@ -0,0 +1,224 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.nova.v1_1.options;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Objects.toStringHelper;
import static com.google.common.base.Preconditions.*;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.inject.Inject;
import org.jclouds.encryption.internal.Base64;
import org.jclouds.http.HttpRequest;
import org.jclouds.openstack.nova.v1_1.domain.SecurityGroup;
import org.jclouds.rest.MapBinder;
import org.jclouds.rest.binders.BindToJsonPayload;
import org.jclouds.util.Preconditions2;
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;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.gson.annotations.SerializedName;
/**
* @author Adam Lowe
*/
public class CreateVolumeOptions implements MapBinder {
public static final CreateVolumeOptions NONE = new CreateVolumeOptions();
@Inject
private BindToJsonPayload jsonBinder;
private String name;
private String description;
private String volumeType;
private String availabilityZone;
private String snapshotId;
private Map<String, String> metadata = ImmutableMap.of();
@Override
public <R extends HttpRequest> R bindToRequest(R request, Map<String, String> 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 (!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;
}
public CreateVolumeOptions name(String name) {
this.name = name;
return this;
}
public CreateVolumeOptions description(String description) {
this.description = description;
return this;
}
public CreateVolumeOptions volumeType(String volumeType) {
this.volumeType = volumeType;
return this;
}
public CreateVolumeOptions availabilityZone(String availabilityZone) {
this.availabilityZone = availabilityZone;
return this;
}
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,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.nova.v1_1.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 CreateVolumeSnapshotOptions implements MapBinder {
public static final CreateVolumeSnapshotOptions NONE = new CreateVolumeSnapshotOptions();
@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, String> postParams) {
Map<String, String> 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 CreateVolumeSnapshotOptions)) return false;
final CreateVolumeSnapshotOptions other = CreateVolumeSnapshotOptions.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 CreateVolumeSnapshotOptions name(String name) {
this.name = name;
return this;
}
public CreateVolumeSnapshotOptions description(String description) {
this.description = description;
return this;
}
public CreateVolumeSnapshotOptions 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 CreateVolumeSnapshotOptions#getName()
*/
public static CreateVolumeSnapshotOptions name(String name) {
return new CreateVolumeSnapshotOptions().name(name);
}
/**
* @see CreateVolumeSnapshotOptions#getDescription()
*/
public static CreateVolumeSnapshotOptions description(String description) {
return new CreateVolumeSnapshotOptions().description(description);
}
/**
* @see CreateVolumeSnapshotOptions#isForce()
*/
public static CreateVolumeSnapshotOptions force() {
return new CreateVolumeSnapshotOptions().force();
}
}
}

View File

@ -0,0 +1,458 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.nova.v1_1.extensions;
import static org.testng.Assert.*;
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.openstack.nova.v1_1.domain.Volume;
import org.jclouds.openstack.nova.v1_1.domain.VolumeAttachment;
import org.jclouds.openstack.nova.v1_1.domain.VolumeSnapshot;
import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest;
import org.jclouds.openstack.nova.v1_1.options.CreateVolumeOptions;
import org.jclouds.openstack.nova.v1_1.options.CreateVolumeSnapshotOptions;
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 VolumeClient guice wiring and parsing
*
* @author Adam Lowe
*/
@Test(groups = "unit", testName = "VolumeClientExpectTest")
public class VolumeClientExpectTest extends BaseNovaClientExpectTest {
private DateService dateService = new SimpleDateFormatDateService();
public void testListVolumes() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).build(),
standardResponseBuilder(200).payload(payloadFromResource("/volume_list.json")).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
Set<Volume> volumes = client.listVolumes();
assertEquals(volumes, ImmutableSet.of(testVolume()));
}
public void testListVolumesFail() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).build(),
standardResponseBuilder(404).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
Set<Volume> volumes = client.listVolumes();
assertTrue(volumes.isEmpty());
}
public void testListVolumesInDetail() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes/detail");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).build(),
standardResponseBuilder(200).payload(payloadFromResource("/volume_list_detail.json")).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
Set<Volume> volumes = client.listVolumesInDetail();
assertEquals(volumes, ImmutableSet.of(testVolume()));
}
public void testListVolumesInDetailFail() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes/detail");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).build(),
standardResponseBuilder(404).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
Set<Volume> volumes = client.listVolumesInDetail();
assertTrue(volumes.isEmpty());
}
public void testCreateVolume() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint)
.method("POST")
.payload(payloadFromStringWithContentType("{\"volume\":{\"display_name\":\"jclouds-test-volume\",\"display_description\":\"description of test volume\",\"size\":\"1\"}}", MediaType.APPLICATION_JSON))
.build(),
standardResponseBuilder(200).payload(payloadFromResource("/volume_details.json")).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
Volume volume = client.createVolume(1, CreateVolumeOptions.Builder.name("jclouds-test-volume").description("description of test volume"));
assertEquals(volume, testVolume());
}
@Test(expectedExceptions = ResourceNotFoundException.class)
public void testCreateVolumeFail() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint)
.endpoint(endpoint)
.method("POST")
.payload(payloadFromStringWithContentType("{\"volume\":{\"display_name\":\"jclouds-test-volume\",\"display_description\":\"description of test volume\",\"size\":\"1\"}}", MediaType.APPLICATION_JSON))
.build(),
standardResponseBuilder(404).payload(payloadFromResource("/volume_details.json")).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
client.createVolume(1, CreateVolumeOptions.Builder.name("jclouds-test-volume").description("description of test volume"));
}
public void testGetVolume() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes/1");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).build(),
standardResponseBuilder(200).payload(payloadFromResource("/volume_details.json")).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
Volume volume = client.getVolume("1");
assertEquals(volume, testVolume());
// double-check equals()
assertEquals(volume.getStatus(), Volume.Status.IN_USE);
assertEquals(volume.getDescription(), "This is a test volume");
assertEquals(volume.getZone(), "nova");
assertEquals(volume.getName(), "test");
assertEquals(volume.getStatus(), Volume.Status.IN_USE);
assertEquals(Iterables.getOnlyElement(volume.getAttachments()), testAttachment());
}
public void testGetVolumeFail() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes/1");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).build(),
standardResponseBuilder(404).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
assertNull(client.getVolume("1"));
}
public void testDeleteVolume() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes/1");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).method("DELETE").build(),
standardResponseBuilder(200).payload(payloadFromResource("/attachment_details.json")).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
assertTrue(client.deleteVolume("1"));
}
public void testDeleteVolumeFail() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes/1");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).method("DELETE").build(),
standardResponseBuilder(404).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
assertFalse(client.deleteVolume("1"));
}
public void testListAttachments() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/instance-1/os-volume_attachments");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).build(),
standardResponseBuilder(200).payload(payloadFromResource("/attachment_list.json")).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
Set<VolumeAttachment> attachments = client.listAttachmentsOnServer("instance-1");
assertEquals(attachments, ImmutableSet.of(testAttachment()));
// double-check individual fields
VolumeAttachment attachment = Iterables.getOnlyElement(attachments);
assertEquals(attachment.getDevice(), "/dev/vdc");
assertEquals(attachment.getServerId(), "b4785058-cb80-491b-baa3-e4ee6546450e");
assertEquals(attachment.getId(), "1");
assertEquals(attachment.getVolumeId(), "1");
}
@Test(expectedExceptions = AuthorizationException.class)
public void testListAttachmentsFail() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/instance-2/os-volume_attachments");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).build(),
standardResponseBuilder(401).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
client.listAttachmentsOnServer("instance-2");
}
public void testGetAttachment() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/instance-1/os-volume_attachments/1");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).build(),
standardResponseBuilder(200).payload(payloadFromResource("/attachment_details.json")).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
VolumeAttachment attachment = client.getAttachmentForVolumeOnServer("1", "instance-1");
assertEquals(attachment, testAttachment());
}
public void testGetAttachmentFail() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/instance-1/os-volume_attachments/1");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).build(),
standardResponseBuilder(404).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
assertNull(client.getAttachmentForVolumeOnServer("1", "instance-1"));
}
public void testAttachVolume() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/instance-1/os-volume_attachments");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).method("POST")
.payload(payloadFromStringWithContentType("{\"volumeAttachment\":{\"volumeId\":\"1\",\"device\":\"/dev/vdc\"}}", MediaType.APPLICATION_JSON)).endpoint(endpoint).build(),
standardResponseBuilder(200).payload(payloadFromResource("/attachment_details.json")).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
VolumeAttachment result = client.attachVolumeToServerAsDevice("1", "instance-1", "/dev/vdc");
assertEquals(result, testAttachment());
}
@Test(expectedExceptions = ResourceNotFoundException.class)
public void testAttachVolumeFail() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/instance-1/os-volume_attachments");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).method("POST")
.payload(payloadFromStringWithContentType("{\"volumeAttachment\":{\"volumeId\":\"1\",\"device\":\"/dev/vdc\"}}", MediaType.APPLICATION_JSON)).endpoint(endpoint).build(),
standardResponseBuilder(404).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
client.attachVolumeToServerAsDevice("1", "instance-1","/dev/vdc");
}
public void testDetachVolume() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/instance-1/os-volume_attachments/1");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).method("DELETE").build(),
standardResponseBuilder(200).payload(payloadFromResource("/attachment_details.json")).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
assertTrue(client.detachVolumeFromServer("1", "instance-1"));
}
public void testDetachVolumeFail() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/instance-1/os-volume_attachments/1");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).method("DELETE").build(),
standardResponseBuilder(404).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
assertFalse(client.detachVolumeFromServer("1", "instance-1"));
}
public void testListSnapshots() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).build(),
standardResponseBuilder(200).payload(payloadFromResource("/snapshot_list.json")).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
Set<VolumeSnapshot> snapshots = client.listSnapshots();
assertEquals(snapshots, ImmutableSet.of(testSnapshot()));
}
public void testListSnapshotsFail() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).build(),
standardResponseBuilder(404).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
Set<VolumeSnapshot> snapshots = client.listSnapshots();
assertTrue(snapshots.isEmpty());
}
public void testGetSnapshot() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots/1");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).build(),
standardResponseBuilder(200).payload(payloadFromResource("/snapshot_details.json")).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
VolumeSnapshot snapshot = client.getSnapshot("1");
assertEquals(snapshot, testSnapshot());
}
public void testGetSnapshotFail() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots/1");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).build(),
standardResponseBuilder(404).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
assertNull(client.getSnapshot("1"));
}
public void testListSnapshotsInDetail() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots/detail");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).build(),
standardResponseBuilder(200).payload(payloadFromResource("/snapshot_list_detail.json")).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
Set<VolumeSnapshot> snapshots = client.listSnapshotsInDetail();
assertEquals(snapshots, ImmutableSet.of(testSnapshot()));
// double-check individual fields
VolumeSnapshot snappy = Iterables.getOnlyElement(snapshots);
assertEquals(snappy.getId(), "7");
assertEquals(snappy.getVolumeId(), "9");
assertEquals(snappy.getStatus(), Volume.Status.AVAILABLE);
assertEquals(snappy.getDescription(), "jclouds live test snapshot");
assertEquals(snappy.getName(), "jclouds-live-test");
assertEquals(snappy.getSize(), 1);
}
public void testListSnapshotsInDetailFail() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots/detail");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).build(),
standardResponseBuilder(404).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
Set<VolumeSnapshot> snapshots = client.listSnapshotsInDetail();
assertTrue(snapshots.isEmpty());
}
public void testCreateSnapshot() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint)
.method("POST")
.payload(payloadFromStringWithContentType("{\"snapshot\":{\"display_name\":\"jclouds-live-test\",\"volume_id\":\"13\",\"display_description\":\"jclouds live test snapshot\",\"force\":\"true\"}}", MediaType.APPLICATION_JSON))
.build(),
standardResponseBuilder(200).payload(payloadFromResource("/snapshot_details.json")).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
VolumeSnapshot snapshot = client.createSnapshot("13", CreateVolumeSnapshotOptions.Builder.name("jclouds-live-test").description("jclouds live test snapshot").force());
assertEquals(snapshot, testSnapshot());
}
@Test(expectedExceptions = AuthorizationException.class)
public void testCreateSnapshotFail() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint)
.method("POST")
.payload(payloadFromStringWithContentType("{\"snapshot\":{\"display_name\":\"jclouds-live-test\",\"volume_id\":\"13\",\"display_description\":\"jclouds live test snapshot\",\"force\":\"true\"}}", MediaType.APPLICATION_JSON))
.build(),
standardResponseBuilder(401).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
client.createSnapshot("13", CreateVolumeSnapshotOptions.Builder.name("jclouds-live-test").description("jclouds live test snapshot").force());
}
public void testDeleteSnapshot() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots/1");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).method("DELETE").build(),
standardResponseBuilder(200).payload(payloadFromResource("/snapshot_details.json")).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
assertTrue(client.deleteSnapshot("1"));
}
@Test(expectedExceptions = AuthorizationException.class)
public void testDeleteSnapshotFail() {
URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots/1");
VolumeClient client = requestsSendResponses(
keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse,
standardRequestBuilder(endpoint).method("DELETE").build(),
standardResponseBuilder(401).build()
).getVolumeExtensionForZone("az-1.region-a.geo-1").get();
client.deleteSnapshot("1");
}
protected Volume testVolume() {
return Volume.builder().status(Volume.Status.IN_USE).description("This is a test volume").zone("nova").name("test")
.attachments(ImmutableSet.of(testAttachment())).size(1).id("1").created(dateService.iso8601SecondsDateParse("2012-04-23 12:16:45")).build();
}
protected VolumeAttachment testAttachment() {
return VolumeAttachment.builder().device("/dev/vdc").serverId("b4785058-cb80-491b-baa3-e4ee6546450e").id("1").volumeId("1").build();
}
protected VolumeSnapshot testSnapshot() {
return VolumeSnapshot.builder().id("7").volumeId("9").description("jclouds live test snapshot").status(Volume.Status.AVAILABLE)
.name("jclouds-live-test").size(1).created(dateService.iso8601SecondsDateParse("2012-04-24 13:34:42")).build();
}
}

View File

@ -0,0 +1,268 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.nova.v1_1.extensions;
import static org.testng.Assert.*;
import java.util.Set;
import org.jclouds.openstack.domain.Resource;
import org.jclouds.openstack.nova.v1_1.domain.VolumeAttachment;
import org.jclouds.openstack.nova.v1_1.domain.VolumeSnapshot;
import org.jclouds.openstack.nova.v1_1.domain.Volume;
import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientLiveTest;
import org.jclouds.openstack.nova.v1_1.options.CreateVolumeSnapshotOptions;
import org.jclouds.openstack.nova.v1_1.options.CreateVolumeOptions;
import org.jclouds.predicates.RetryablePredicate;
import org.testng.annotations.AfterGroups;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
/**
* Tests behavior of VolumeClient
*
* @author Adam Lowe
*/
@Test(groups = "live", testName = "VolumeClientLiveTest", singleThreaded = true)
public class VolumeClientLiveTest extends BaseNovaClientLiveTest {
private VolumeClient client;
private String zone;
private Volume testVolume;
private VolumeSnapshot testSnapshot;
@BeforeGroups(groups = { "integration", "live" })
@Override
public void setupContext() {
super.setupContext();
zone = Iterables.getFirst(novaContext.getApi().getConfiguredZones(), "nova");
Optional<VolumeClient> optClient = novaContext.getApi().getVolumeExtensionForZone(zone);
if (optClient.isPresent()) {
client = optClient.get();
}
}
@AfterGroups(groups = "live", alwaysRun = true)
@Override
protected void tearDown() {
if (client != null) {
if (testSnapshot != null) {
final String snapshotId = testSnapshot.getId();
assertTrue(client.deleteSnapshot(snapshotId));
assertTrue(new RetryablePredicate<VolumeClient>(new Predicate<VolumeClient>() {
@Override
public boolean apply(VolumeClient volumeClient) {
return client.getSnapshot(snapshotId) == null;
}
}, 30 * 1000L).apply(client));
}
if (testVolume != null) {
final String volumeId = testVolume.getId();
assertTrue(client.deleteVolume(volumeId));
assertTrue(new RetryablePredicate<VolumeClient>(new Predicate<VolumeClient>() {
@Override
public boolean apply(VolumeClient volumeClient) {
return client.getVolume(volumeId) == null;
}
}, 180 * 1000L).apply(client));
}
}
super.tearDown();
}
public void testCreateVolume() throws Exception {
testVolume = client.createVolume(1, CreateVolumeOptions.Builder.name("jclouds-test-volume").description("description of test volume").availabilityZone(zone));
for (int i=0; i<100 && testVolume.getStatus() == Volume.Status.CREATING; i++) {
Thread.sleep(100);
testVolume = client.getVolume(testVolume.getId());
}
assertEquals(testVolume.getStatus(), Volume.Status.AVAILABLE);
}
@Test(dependsOnMethods = "testCreateVolume")
public void testListVolumes() throws Exception {
if (client != null) {
Set<Volume> volumes = client.listVolumes();
assertNotNull(volumes);
boolean foundIt = false;
for (Volume vol : volumes) {
Volume details = client.getVolume(vol.getId());
assertNotNull(details);
if (Objects.equal(details.getId(), testVolume.getId())) {
foundIt = true;
}
}
assertTrue(foundIt, "Failed to find the volume we created in listVolumes() response");
}
}
@Test(dependsOnMethods = "testCreateVolume")
public void testListVolumesInDetail() throws Exception {
if (client != null) {
Set<Volume> volumes = client.listVolumesInDetail();
assertNotNull(volumes);
assertTrue(volumes.contains(testVolume));
boolean foundIt = false;
for (Volume vol : volumes) {
Volume details = client.getVolume(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 listVolumesInDetail() response");
}
}
@Test(dependsOnMethods = "testCreateVolume")
public void testCreateSnapshot() throws Exception {
if (client != null) {
testSnapshot = client.createSnapshot(testVolume.getId(), CreateVolumeSnapshotOptions.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<VolumeClient>(new Predicate<VolumeClient>() {
@Override
public boolean apply(VolumeClient volumeClient) {
return client.getSnapshot(snapshotId).getStatus() == Volume.Status.AVAILABLE;
}
}, 30 * 1000L).apply(client));
}
}
@Test(dependsOnMethods = "testCreateSnapshot")
public void testListSnapshots() throws Exception {
if (client != null) {
Set<VolumeSnapshot> snapshots = client.listSnapshots();
assertNotNull(snapshots);
boolean foundIt = false;
for (VolumeSnapshot snap : snapshots) {
VolumeSnapshot details = client.getSnapshot(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() throws Exception {
if (client != null) {
Set<VolumeSnapshot> snapshots = client.listSnapshotsInDetail();
assertNotNull(snapshots);
boolean foundIt = false;
for (VolumeSnapshot snap : snapshots) {
VolumeSnapshot details = client.getSnapshot(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(VolumeSnapshot a, VolumeSnapshot 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());
}
@Test(enabled=false, dependsOnMethods = "testCreateVolume") // disabled as it alters an existing server
public void testAttachments() throws Exception {
if (client != null) {
Set<Resource> servers = novaContext.getApi().getServerClientForZone(zone).listServers();
if (!servers.isEmpty()) {
final String serverId = Iterables.getFirst(servers, null).getId();
Set<VolumeAttachment> attachments = client.listAttachmentsOnServer(serverId);
assertNotNull(attachments);
final int before = attachments.size();
VolumeAttachment testAttachment = client.attachVolumeToServerAsDevice(testVolume.getId(), serverId, "/dev/vdf");
assertNotNull(testAttachment.getId());
assertEquals(testAttachment.getVolumeId(), testVolume.getId());
assertTrue(new RetryablePredicate<VolumeClient>(new Predicate<VolumeClient>() {
@Override
public boolean apply(VolumeClient volumeClient) {
return client.listAttachmentsOnServer(serverId).size() == before+1;
}
}, 60 * 1000L).apply(client));
attachments = client.listAttachmentsOnServer(serverId);
assertNotNull(attachments);
assertEquals(attachments.size(), before+1);
assertEquals(client.getVolume(testVolume.getId()).getStatus(), Volume.Status.IN_USE);
boolean foundIt = false;
for (VolumeAttachment att : attachments) {
VolumeAttachment details = client.getAttachmentForVolumeOnServer(att.getVolumeId(), serverId);
assertNotNull(details);
assertNotNull(details.getId());
assertNotNull(details.getServerId());
assertNotNull(details.getVolumeId());
if (Objects.equal(details.getVolumeId(), testVolume.getId())) {
foundIt = true;
assertEquals(details.getDevice(), "/dev/vdf");
assertEquals(details.getServerId(), serverId);
}
}
assertTrue(foundIt, "Failed to find the attachment we created in listAttachments() response");
client.detachVolumeFromServer(testVolume.getId(), serverId);
assertTrue(new RetryablePredicate<VolumeClient>(new Predicate<VolumeClient>() {
@Override
public boolean apply(VolumeClient volumeClient) {
return client.listAttachmentsOnServer(serverId).size() == before;
}
}, 60 * 1000L).apply(client));
}
}
}
}

View File

@ -20,6 +20,8 @@ package org.jclouds.openstack.nova.v1_1.internal;
import java.net.URI;
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;
@ -68,4 +70,14 @@ public class BaseNovaExpectTest<T> extends BaseRestClientExpectTest<T> {
unmatchedExtensionsOfNovaResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource("/extension_list.json")).build();
}
protected HttpRequest.Builder standardRequestBuilder(URI endpoint) {
return HttpRequest.builder().method("GET")
.headers(ImmutableMultimap.of("Accept", MediaType.APPLICATION_JSON, "X-Auth-Token", authToken))
.endpoint(endpoint);
}
protected HttpResponse.Builder standardResponseBuilder(int status) {
return HttpResponse.builder().statusCode(status);
}
}

View File

@ -0,0 +1 @@
{"volumeAttachment": {"device": "/dev/vdc", "serverId": "b4785058-cb80-491b-baa3-e4ee6546450e", "id": 1, "volumeId": 1}}

View File

@ -0,0 +1 @@
{"volumeAttachments": [{"device": "/dev/vdc", "serverId": "b4785058-cb80-491b-baa3-e4ee6546450e", "id": 1, "volumeId": 1}]}

View File

@ -0,0 +1 @@
{"snapshot": {"status": "available", "displayDescription": "jclouds live test snapshot", "displayName": "jclouds-live-test", "volumeId": 9, "id": 7, "createdAt": "2012-04-24 13:34:42", "size": 1}}

View File

@ -0,0 +1 @@
{"snapshots": [{"status": "available", "displayDescription": "jclouds live test snapshot", "displayName": "jclouds-live-test", "volumeId": 9, "id": 7, "createdAt": "2012-04-24 13:34:42", "size": 1}]}

View File

@ -0,0 +1 @@
{"snapshots": [{"status": "available", "displayDescription": "jclouds live test snapshot", "displayName": "jclouds-live-test", "volumeId": 9, "id": 7, "createdAt": "2012-04-24 13:34:42", "size": 1}]}

View File

@ -0,0 +1 @@
{"volume": {"status": "in-use", "displayDescription": "This is a test volume", "availabilityZone": "nova", "displayName": "test", "attachments": [{"device": "/dev/vdc", "serverId": "b4785058-cb80-491b-baa3-e4ee6546450e", "id": 1, "volumeId": 1}], "volumeType": null, "snapshotId": null, "size": 1, "id": 1, "createdAt": "2012-04-23 12:16:45", "metadata": {}}}

View File

@ -0,0 +1 @@
{"volumes": [{"status": "in-use", "displayDescription": "This is a test volume", "availabilityZone": "nova", "displayName": "test", "attachments": [{"device": "/dev/vdc", "serverId": "b4785058-cb80-491b-baa3-e4ee6546450e", "id": 1, "volumeId": 1}], "volumeType": null, "snapshotId": null, "size": 1, "id": 1, "createdAt": "2012-04-23 12:16:45", "metadata": {}}]}

View File

@ -0,0 +1 @@
{"volumes": [{"status": "in-use", "displayDescription": "This is a test volume", "availabilityZone": "nova", "displayName": "test", "attachments": [{"device": "/dev/vdc", "serverId": "b4785058-cb80-491b-baa3-e4ee6546450e", "id": 1, "volumeId": 1}], "volumeType": null, "snapshotId": null, "size": 1, "id": 1, "createdAt": "2012-04-23 12:16:45", "metadata": {}}]}