VolumeClient Implementation - Attach/Detach/CreateFromSnapshot and CreatefromVolume with parrial tests.

This commit is contained in:
vijaykiran 2011-11-15 12:14:30 +01:00
parent e6090530be
commit 5e27696481
9 changed files with 236 additions and 36 deletions

View File

@ -34,6 +34,7 @@ import org.jclouds.cloudstack.features.NetworkAsyncClient;
import org.jclouds.cloudstack.features.OfferingAsyncClient;
import org.jclouds.cloudstack.features.SSHKeyPairAsyncClient;
import org.jclouds.cloudstack.features.SecurityGroupAsyncClient;
import org.jclouds.cloudstack.features.SnapshotAsyncClient;
import org.jclouds.cloudstack.features.TemplateAsyncClient;
import org.jclouds.cloudstack.features.VMGroupAsyncClient;
import org.jclouds.cloudstack.features.VirtualMachineAsyncClient;
@ -177,4 +178,10 @@ public interface CloudStackAsyncClient {
*/
@Delegate
VolumeAsyncClient getVolumeClient();
/**
* Provides asynchronous access to Snapshots
*/
@Delegate
SnapshotAsyncClient getSnapshotClient();
}

View File

@ -36,6 +36,7 @@ import org.jclouds.cloudstack.features.NetworkClient;
import org.jclouds.cloudstack.features.OfferingClient;
import org.jclouds.cloudstack.features.SSHKeyPairClient;
import org.jclouds.cloudstack.features.SecurityGroupClient;
import org.jclouds.cloudstack.features.SnapshotClient;
import org.jclouds.cloudstack.features.TemplateClient;
import org.jclouds.cloudstack.features.VMGroupClient;
import org.jclouds.cloudstack.features.VirtualMachineClient;
@ -180,4 +181,10 @@ public interface CloudStackClient {
*/
@Delegate
VolumeClient getVolumeClient();
/**
* Provides synchronous access to Snapshots
*/
@Delegate
SnapshotClient getSnapshotClient();
}

View File

@ -55,6 +55,8 @@ import org.jclouds.cloudstack.features.SSHKeyPairAsyncClient;
import org.jclouds.cloudstack.features.SSHKeyPairClient;
import org.jclouds.cloudstack.features.SecurityGroupAsyncClient;
import org.jclouds.cloudstack.features.SecurityGroupClient;
import org.jclouds.cloudstack.features.SnapshotAsyncClient;
import org.jclouds.cloudstack.features.SnapshotClient;
import org.jclouds.cloudstack.features.TemplateAsyncClient;
import org.jclouds.cloudstack.features.TemplateClient;
import org.jclouds.cloudstack.features.VMGroupAsyncClient;
@ -107,6 +109,7 @@ public class CloudStackRestClientModule extends RestClientModule<CloudStackClien
.put(VMGroupClient.class, VMGroupAsyncClient.class)//
.put(ISOClient.class, ISOAsyncClient.class)//
.put(VolumeClient.class, VolumeAsyncClient.class)//
.put(SnapshotClient.class, SnapshotAsyncClient.class)//
.build();
public CloudStackRestClientModule() {

View File

@ -19,8 +19,14 @@
package org.jclouds.cloudstack.domain;
import java.util.Date;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Date;
import java.util.Map;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.gson.annotations.SerializedName;
/**
@ -58,8 +64,7 @@ public class Volume implements Comparable<Volume> {
private String state;
private String storage;
private String storageType;
//TODO Change to enum
private String type;
private VolumeType type;
private long virtualMachineId;
private String vmDisplayName;
private String vmName;
@ -182,7 +187,7 @@ public class Volume implements Comparable<Volume> {
return this;
}
public Builder type(String type) {
public Builder type(VolumeType type) {
this.type = type;
return this;
}
@ -268,8 +273,7 @@ public class Volume implements Comparable<Volume> {
private String storage;
@SerializedName("storagetype")
private String storageType;
//TODO Change to enum
private String type;
private VolumeType type;
@SerializedName("virtualmachineid")
private long virtualMachineId;
@SerializedName("vmdisplayname")
@ -288,7 +292,7 @@ public class Volume implements Comparable<Volume> {
String domain, long domainId, String hypervisor, boolean extractable, long jobId,
String jobStatus, String name, String serviceOfferingDisplayText, long serviceOfferingId,
String serviceOfferingName, long size, long snapshotId, String state, String storage,
String storageType, String type, long virtualMachineId, String vmDisplayName, String vmName,
String storageType, VolumeType type, long virtualMachineId, String vmDisplayName, String vmName,
VirtualMachine.State vmState, long zoneId, String zoneName) {
this.id = id;
this.attached = attached;
@ -415,7 +419,7 @@ public class Volume implements Comparable<Volume> {
return storageType;
}
public String getType() {
public VolumeType getType() {
return type;
}
@ -530,4 +534,38 @@ public class Volume implements Comparable<Volume> {
result = 31 * result + (zoneName != null ? zoneName.hashCode() : 0);
return result;
}
public enum VolumeType {
ROOT(0),
DATADISK(1),
UNRECOGNIZED(Integer.MAX_VALUE);
private int code;
private static final Map<Integer, VolumeType> INDEX = Maps.uniqueIndex(ImmutableSet.copyOf(VolumeType.values()),
new Function<VolumeType, Integer>() {
@Override
public Integer apply(VolumeType input) {
return input.code;
}
});
VolumeType(int code) {
this.code = code;
}
@Override
public String toString() {
return name();
}
public static VolumeType fromValue(String resourceType) {
Integer code = new Integer(checkNotNull(resourceType, "resourcetype"));
return INDEX.containsKey(code) ? INDEX.get(code) : UNRECOGNIZED;
}
}
}

View File

@ -33,8 +33,8 @@ import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.QueryParams;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.SelectJson;
import org.jclouds.rest.annotations.Unwrap;
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
/**
@ -64,23 +64,41 @@ public interface VolumeAsyncClient {
*/
@GET
@QueryParams(keys = "command", values = "createVolume")
@SelectJson("volume")
@Unwrap
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<AsyncCreateResponse> createVolumeFromDiskOfferingInZone(@QueryParam("name") String name,
@QueryParam("diskofferingid") long diskOfferingId,
@QueryParam("zoneid") long zoneId);
/**
* @see VolumeClient#createVolumeWithSnapshot(String, long)
* @see VolumeClient#createVolumeFromSnapshotInZone(String, long, long)
*/
@GET
@QueryParams(keys = "command", values = "createVolume")
@SelectJson("volume")
@Unwrap
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<AsyncCreateResponse> createVolumeWithSnapshot(@QueryParam("name") String name,
@QueryParam("snapshotid") long diskOfferingId);
ListenableFuture<AsyncCreateResponse> createVolumeFromSnapshotInZone(@QueryParam("name") String name,
@QueryParam("snapshotid") long snapshotId,
@QueryParam("zoneid") long zoneId);
/**
* @see VolumeClient#attachVolume(long, long)
*/
@GET
@QueryParams(keys = "command", values = "attachVolume")
@Unwrap
@Consumes(MediaType.APPLICATION_JSON)
ListenableFuture<AsyncCreateResponse> attachVolume(@QueryParam("id") long volumeId,
@QueryParam("virtualmachineid") long virtualMachineId);
/**
* @see VolumeClient#detachVolume(long)
*/
@GET
@QueryParams(keys = "command", values = "detachVolume")
@Unwrap
@Consumes(MediaType.APPLICATION_JSON)
ListenableFuture<AsyncCreateResponse> detachVolume(@QueryParam("id") long volumeId);
/**
* @see VolumeClient#deleteVolume(long)

View File

@ -42,7 +42,7 @@ public interface VolumeClient {
* @param name name of the volume
* @param diskOfferingId the ID of the disk offering.
* @param zoneId the ID of the availability zone
* @return Volume
* @return AsyncCreateResponse job response used to track creation
*/
AsyncCreateResponse createVolumeFromDiskOfferingInZone(String name, long diskOfferingId, long zoneId);
@ -51,9 +51,10 @@ public interface VolumeClient {
*
* @param name name of the volume
* @param snapshotId Snapshot id to be used while creating the volume
* @return Volume
* @param zoneId the ID of the availability zone
* @return AsyncCreateResponse job response used to track creation
*/
AsyncCreateResponse createVolumeWithSnapshot(String name, long snapshotId);
AsyncCreateResponse createVolumeFromSnapshotInZone(String name, long snapshotId, long zoneId);
/**
* List volumes
@ -69,4 +70,21 @@ public interface VolumeClient {
*/
void deleteVolume(long id);
/**
* Attaches a disk volume to a virtual machine.
*
* @param volumeId the ID of the disk volume
* @param virtualMachineId the ID of the virtual machine
* @return AsyncCreateResponse job response used to track creation
*/
AsyncCreateResponse attachVolume(long volumeId, long virtualMachineId);
/**
* Detaches a disk volume to a virtual machine.
*
* @param volumeId the ID of the disk volume
* @return AsyncCreateResponse job response used to track creation
*/
AsyncCreateResponse detachVolume(long volumeId);
}

View File

@ -63,6 +63,7 @@ public class CloudStackAsyncClientTest extends BaseCloudStackAsyncClientTest<Clo
assert syncClient.getLimitClient() != null;
assert syncClient.getISOClient() != null;
assert syncClient.getVolumeClient() != null;
assert syncClient.getSnapshotClient() != null;
}
public void testAsync() throws SecurityException, NoSuchMethodException, InterruptedException, ExecutionException {
@ -87,6 +88,7 @@ public class CloudStackAsyncClientTest extends BaseCloudStackAsyncClientTest<Clo
assert asyncClient.getLimitClient() != null;
assert asyncClient.getISOClient() != null;
assert asyncClient.getVolumeClient() != null;
assert asyncClient.getSnapshotClient() != null;
}

View File

@ -25,7 +25,6 @@ import com.google.inject.TypeLiteral;
import org.jclouds.cloudstack.options.ListVolumesOptions;
import org.jclouds.http.HttpRequest;
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
import org.jclouds.rest.internal.RestAnnotationProcessor;
import org.testng.annotations.Test;
@ -59,46 +58,76 @@ public class VolumeAsyncClientTest extends BaseCloudStackAsyncClientTest<VolumeA
}
public void testCreateVolumeWithSnapshot() throws SecurityException, NoSuchMethodException, IOException {
Method method = VolumeAsyncClient.class.getMethod("createVolumeWithSnapshot", String.class, Long.class);
Method method = VolumeAsyncClient.class.getMethod("createVolumeWithSnapshot", String.class, long.class);
HttpRequest httpRequest = processor.createRequest(method, prefix + "-jclouds-volume", 999L);
assertRequestLineEquals(httpRequest,
"GET http://localhost:8080/client/api?response=json&command=createVolume&name="
+ prefix +"-jclouds-volume&snapshotid=999 HTTP/1.1");
+ prefix + "-jclouds-volume&snapshotid=999 HTTP/1.1");
assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null, null, false);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class);
checkFilters(httpRequest);
}
public void testCreateVolumeFromDiskOffering() throws SecurityException, NoSuchMethodException, IOException {
Method method = VolumeAsyncClient.class.getMethod("createVolumeFromDiskOfferingInZone",
String.class, long.class , long.class);
String.class, long.class, long.class);
HttpRequest httpRequest = processor.createRequest(method, prefix + "-jclouds-volume", 999L, 111L);
assertRequestLineEquals(httpRequest,
"GET http://localhost:8080/client/api?response=json&command=createVolume&name="
+ prefix +"-jclouds-volume&zoneid=111&diskofferingid=999 HTTP/1.1");
+ prefix + "-jclouds-volume&zoneid=111&diskofferingid=999 HTTP/1.1");
assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null, null, false);
assertSaxResponseParserClassEquals(method, null);
checkFilters(httpRequest);
}
public void testAttachVolume() throws SecurityException, NoSuchMethodException, IOException {
Method method = VolumeAsyncClient.class.getMethod("attachVolume",
long.class, long.class);
HttpRequest httpRequest = processor.createRequest(method, 111L, 999L);
assertRequestLineEquals(httpRequest,
"GET http://localhost:8080/client/api?response=json&command=attachVolume&id=111&virtualmachineid=999 HTTP/1.1");
assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null, null, false);
assertSaxResponseParserClassEquals(method, null);
checkFilters(httpRequest);
}
public void testDetachVolume() throws SecurityException, NoSuchMethodException, IOException {
Method method = VolumeAsyncClient.class.getMethod("detachVolume", long.class);
HttpRequest httpRequest = processor.createRequest(method, 111L);
assertRequestLineEquals(httpRequest,
"GET http://localhost:8080/client/api?response=json&command=detachVolume&id=111 HTTP/1.1");
assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null, null, false);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class);
checkFilters(httpRequest);
}
public void testDeleteVolume() throws SecurityException, NoSuchMethodException, IOException {
Method method = VolumeAsyncClient.class.getMethod("deleteVolume", Long.class);
Method method = VolumeAsyncClient.class.getMethod("deleteVolume", long.class);
HttpRequest httpRequest = processor.createRequest(method, prefix + "-jclouds-volume");
assertRequestLineEquals(httpRequest,
"GET http://localhost:8080/client/api?response=json&command=deleteVolume&id="
+ prefix +"-jclouds-volume HTTP/1.1");
+ prefix + "-jclouds-volume HTTP/1.1");
assertPayloadEquals(httpRequest, null, null, false);
assertSaxResponseParserClassEquals(method, null);
@ -108,8 +137,6 @@ public class VolumeAsyncClientTest extends BaseCloudStackAsyncClientTest<VolumeA
}
@Override
protected TypeLiteral<RestAnnotationProcessor<VolumeAsyncClient>> createTypeLiteral() {
return new TypeLiteral<RestAnnotationProcessor<VolumeAsyncClient>>() {

View File

@ -20,6 +20,7 @@ package org.jclouds.cloudstack.features;
import static com.google.common.collect.Iterables.find;
import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertNotSame;
import java.util.Set;
@ -40,6 +41,7 @@ public class VolumeClientLiveTest extends BaseCloudStackClientLiveTest {
protected String prefix = System.getProperty("user.name");
private long zoneId;
private long diskOfferingId;
private long snapshotId;
private Volume volume;
public void testListVolumes() {
@ -63,23 +65,101 @@ public class VolumeClientLiveTest extends BaseCloudStackClientLiveTest {
assert jobComplete.apply(job.getJobId());
volume = findVolumeWithId(job.getId());
} catch (IllegalStateException e) {
//TODO Retry ?
e.printStackTrace();
//TODO volume creation failed - retry?
}
}
checkVolume(volume);
//Delete the volume
client.getVolumeClient().deleteVolume(volume.getId());
}
public void testCreateVolumeFromDiskofferingInZoneAndAttachVolumeToVirtualMachineAndDetachAndDelete() {
zoneId = Iterables.getFirst(client.getZoneClient().listZones(), null).getId();
//Pick some disk offering
diskOfferingId = Iterables.getFirst(client.getOfferingClient().listDiskOfferings(), null).getId();
while (volume == null) {
try {
AsyncCreateResponse job = client.getVolumeClient().createVolumeFromDiskOfferingInZone(prefix + "-jclouds-volume",
diskOfferingId, zoneId);
assert jobComplete.apply(job.getJobId());
volume = findVolumeWithId(job.getId());
} catch (IllegalStateException e) {
//TODO volume creation failed - retry?
}
}
checkVolume(volume);
long virtualMachineId = Iterables.getFirst(client.getVirtualMachineClient().listVirtualMachines(), null).getId();
//Attach Volume
Volume attachedVolume = null;
while (attachedVolume == null) {
try {
AsyncCreateResponse job = client.getVolumeClient().attachVolume(volume.getId(), virtualMachineId);
assert jobComplete.apply(job.getJobId());
attachedVolume = findVolumeWithId(volume.getId());
assert attachedVolume.getVirtualMachineId() == virtualMachineId;
assert attachedVolume.getAttached() != null;
} catch (IllegalStateException e) {
//TODO volume creation failed - retry?
}
}
//Detach Volume
Volume detachedVolume = null;
while (detachedVolume == null) {
try {
AsyncCreateResponse job = client.getVolumeClient().detachVolume(volume.getId());
assert jobComplete.apply(job.getJobId());
detachedVolume = findVolumeWithId(volume.getId());
checkVolume(detachedVolume);
} catch (IllegalStateException e) {
//TODO volume creation failed - retry?
}
}
//Cleanup
client.getVolumeClient().deleteVolume(volume.getId());
}
private void checkVolume(Volume volume) {
/*
TODO Uncomment this test after SnapshotClient has test coverage.
public void testCreateVolumeFromSnapshotInZoneAndDeleteVolume() {
zoneId = Iterables.getFirst(client.getZoneClient().listZones(), null).getId();
final Set<Snapshot> snapshots = client.getSnapshotClient().listSnapshots();
assertNotNull(snapshots);
assertNotSame(0, snapshots.size() );
snapshotId = Iterables.getFirst(snapshots, null).getId();
while (volume == null) {
try {
AsyncCreateResponse job = client.getVolumeClient().createVolumeFromSnapshotInZone(prefix + "-jclouds-volume",
snapshotId, zoneId);
assert jobComplete.apply(job.getJobId());
volume = findVolumeWithId(job.getId());
} catch (IllegalStateException e) {
//TODO volume creation failed - retry?
}
}
checkVolume(volume);
//Delete the volume
client.getVolumeClient().deleteVolume(volume.getId());
}
*/
private void checkVolume(final Volume volume) {
assertNotNull(volume.getId());
assertNotNull(volume.getName());
assertNotSame(Volume.VolumeType.UNRECOGNIZED, volume.getType());
}
private Volume findVolumeWithId(final long id) {
System.out.println(id);
return find(client.getVolumeClient().listVolumes(), new Predicate<Volume>() {
@Override