From 0dad4db0a308b92bd414d9878a695538cc59d551 Mon Sep 17 00:00:00 2001 From: vijaykiran Date: Mon, 14 Nov 2011 21:05:36 +0100 Subject: [PATCH 1/3] Volume Client implementation : init --- .../org/jclouds/cloudstack/domain/Volume.java | 533 ++++++++++++++++++ .../features/VolumeAsyncClient.java | 92 +++ .../cloudstack/features/VolumeClient.java | 71 +++ .../options/ListVolumesOptions.java | 218 +++++++ .../features/VolumeAsyncClientTest.java | 117 ++++ .../features/VolumeClientLiveTest.java | 73 +++ 6 files changed, 1104 insertions(+) create mode 100644 apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Volume.java create mode 100644 apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeAsyncClient.java create mode 100644 apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeClient.java create mode 100644 apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListVolumesOptions.java create mode 100644 apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeAsyncClientTest.java create mode 100644 apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeClientLiveTest.java diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Volume.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Volume.java new file mode 100644 index 0000000000..9d4573ea72 --- /dev/null +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Volume.java @@ -0,0 +1,533 @@ +/** + * 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.cloudstack.domain; + +import java.util.Date; + +import com.google.gson.annotations.SerializedName; + +/** + * @author Vijay Kiran + */ +public class Volume implements Comparable { + + public static Builder builder() { + return new Builder(); + } + + + public static class Builder { + + private long id; + private Date attached; + private Date created; + private boolean destroyed; + private long deviceId; + private String diskOfferingDisplayText; + private long diskOfferingId; + private String diskOfferingName; + private String domain; + private long domainId; + private String hypervisor; + private boolean isExtractable; + private long jobId; + private String jobStatus; + private String name; + private String serviceOfferingDisplayText; + private long serviceOfferingId; + private String serviceOfferingName; + private long size; + private long snapshotId; + private String state; + private String storage; + private String storageType; + //TODO Change to enum + private String type; + private long virtualMachineId; + private String vmDisplayName; + private String vmName; + private VirtualMachine.State vmState; + private long zoneId; + private String zoneName; + + public Builder id(long id) { + this.id = id; + return this; + } + + public Builder attached(Date attached) { + this.attached = attached; + return this; + } + + public Builder created(Date created) { + this.created = created; + return this; + } + + public Builder destroyed(boolean destroyed) { + this.destroyed = destroyed; + return this; + } + + public Builder deviceId(long deviceId) { + this.deviceId = deviceId; + return this; + } + + public Builder diskOfferingDisplayText(String diskOfferingDisplayText) { + this.diskOfferingDisplayText = diskOfferingDisplayText; + return this; + } + + public Builder diskOfferingId(long diskOfferingId) { + this.diskOfferingId = diskOfferingId; + return this; + } + + public Builder diskOfferingName(String diskOfferingName) { + this.diskOfferingName = diskOfferingName; + return this; + } + + public Builder domain(String domain) { + this.domain = domain; + return this; + } + + public Builder domainId(long domainId) { + this.domainId = domainId; + return this; + } + + public Builder hypervisor(String hypervisor) { + this.hypervisor = hypervisor; + return this; + } + + public Builder isExtractable(boolean isExtractable) { + this.isExtractable = isExtractable; + return this; + } + + public Builder jobId(long jobId) { + this.jobId = jobId; + return this; + } + + public Builder jobStatus(String jobStatus) { + this.jobStatus = jobStatus; + return this; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder serviceOfferingDisplayText(String serviceOfferingDisplayText) { + this.serviceOfferingDisplayText = serviceOfferingDisplayText; + return this; + } + + public Builder serviceOfferingId(long serviceOfferingId) { + this.serviceOfferingId = serviceOfferingId; + return this; + } + + public Builder serviceOfferingName(String serviceOfferingName) { + this.serviceOfferingName = serviceOfferingName; + return this; + } + + public Builder size(long size) { + this.size = size; + return this; + } + + public Builder snapshotId(long snapshotId) { + this.snapshotId = snapshotId; + return this; + } + + public Builder state(String state) { + this.state = state; + return this; + } + + public Builder storage(String storage) { + this.storage = storage; + return this; + } + + public Builder storageType(String storageType) { + this.storageType = storageType; + return this; + } + + public Builder type(String type) { + this.type = type; + return this; + } + + public Builder virtualMachineId(long virtualMachineId) { + this.virtualMachineId = virtualMachineId; + return this; + } + + public Builder vmDisplayName(String vmDisplayName) { + this.vmDisplayName = vmDisplayName; + return this; + } + + public Builder vmName(String vmName) { + this.vmName = vmName; + return this; + } + + public Builder vmState(VirtualMachine.State vmState) { + this.vmState = vmState; + return this; + } + + public Builder zoneId(long zoneId) { + this.zoneId = zoneId; + return this; + } + + public Builder zoneName(String zoneName) { + this.zoneName = zoneName; + return this; + } + + public Volume build() { + return new Volume(id, attached, created, destroyed, deviceId, diskOfferingDisplayText, + diskOfferingId, diskOfferingName, domain, domainId, hypervisor, isExtractable, + jobId, jobStatus, name, serviceOfferingDisplayText, serviceOfferingId, + serviceOfferingName, size, snapshotId, state, storage, storage, type, virtualMachineId, + vmDisplayName, vmName, vmState, zoneId, zoneName); + } + } + + // for deserialization + Volume() { + + } + + private long id; + private Date attached; + private Date created; + private boolean destroyed; + @SerializedName("deviceid") + private long deviceId; + @SerializedName("diskofferingdisplaytext") + private String diskOfferingDisplayText; + @SerializedName("diskofferingid") + private long diskOfferingId; + @SerializedName("diskofferingname") + private String diskOfferingName; + private String domain; + @SerializedName("domainid") + private long domainId; + private String hypervisor; + @SerializedName("isextractable") + private boolean isExtractable; + @SerializedName("jobid") + private long jobId; + @SerializedName("jobstatus") + //TODO Change to enum + private String jobStatus; + private String name; + @SerializedName("serviceofferingdisplaytext") + private String serviceOfferingDisplayText; + @SerializedName("serviceofferingid") + private long serviceOfferingId; + @SerializedName("serviceofferingname") + private String serviceOfferingName; + private long size; + @SerializedName("snapshotid") + private long snapshotId; + private String state; + private String storage; + @SerializedName("storagetype") + private String storageType; + //TODO Change to enum + private String type; + @SerializedName("virtualmachineid") + private long virtualMachineId; + @SerializedName("vmdisplayname") + private String vmDisplayName; + @SerializedName("vmname") + private String vmName; + @SerializedName("vmstate") + private VirtualMachine.State vmState; + @SerializedName("zoneid") + private long zoneId; + @SerializedName("zonename") + private String zoneName; + + public Volume(long id, Date attached, Date created, boolean destroyed, long deviceId, + String diskOfferingDisplayText, long diskOfferingId, String diskOfferingName, + 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, + VirtualMachine.State vmState, long zoneId, String zoneName) { + this.id = id; + this.attached = attached; + this.created = created; + this.destroyed = destroyed; + this.deviceId = deviceId; + this.diskOfferingDisplayText = diskOfferingDisplayText; + this.diskOfferingId = diskOfferingId; + this.diskOfferingName = diskOfferingName; + this.domain = domain; + this.domainId = domainId; + this.hypervisor = hypervisor; + isExtractable = extractable; + this.jobId = jobId; + this.jobStatus = jobStatus; + this.name = name; + this.serviceOfferingDisplayText = serviceOfferingDisplayText; + this.serviceOfferingId = serviceOfferingId; + this.serviceOfferingName = serviceOfferingName; + this.size = size; + this.snapshotId = snapshotId; + this.state = state; + this.storage = storage; + this.storageType = storageType; + this.type = type; + this.virtualMachineId = virtualMachineId; + this.vmDisplayName = vmDisplayName; + this.vmName = vmName; + this.vmState = vmState; + this.zoneId = zoneId; + this.zoneName = zoneName; + } + + public long getId() { + return id; + } + + public Date getAttached() { + return attached; + } + + + public Date getCreated() { + return created; + } + + public boolean isDestroyed() { + return destroyed; + } + + public long getDeviceId() { + return deviceId; + } + + public String getDiskOfferingDisplayText() { + return diskOfferingDisplayText; + } + + public long getDiskOfferingId() { + return diskOfferingId; + } + + public String getDiskOfferingName() { + return diskOfferingName; + } + + public String getDomain() { + return domain; + } + + public long getDomainId() { + return domainId; + } + + public String getHypervisor() { + return hypervisor; + } + + public boolean isExtractable() { + return isExtractable; + } + + public long getJobId() { + return jobId; + } + + public String getJobStatus() { + return jobStatus; + } + + public String getName() { + return name; + } + + public String getServiceOfferingDisplayText() { + return serviceOfferingDisplayText; + } + + public long getServiceOfferingId() { + return serviceOfferingId; + } + + public String getServiceOfferingName() { + return serviceOfferingName; + } + + public long getSize() { + return size; + } + + public long getSnapshotId() { + return snapshotId; + } + + public String getState() { + return state; + } + + public String getStorage() { + return storage; + } + + public String getStorageType() { + return storageType; + } + + public String getType() { + return type; + } + + public long getVirtualMachineId() { + return virtualMachineId; + } + + public String getVmDisplayName() { + return vmDisplayName; + } + + public String getVmName() { + return vmName; + } + + public VirtualMachine.State getVmState() { + return vmState; + } + + public long getZoneId() { + return zoneId; + } + + public String getZoneName() { + return zoneName; + } + + @Override + public int compareTo(Volume volume) { + return Long.valueOf(this.id).compareTo(volume.id); + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Volume volume = (Volume) o; + + if (deviceId != volume.deviceId) return false; + if (diskOfferingId != volume.diskOfferingId) return false; + if (domainId != volume.domainId) return false; + if (id != volume.id) return false; + if (isExtractable != volume.isExtractable) return false; + if (jobId != volume.jobId) return false; + if (serviceOfferingId != volume.serviceOfferingId) return false; + if (size != volume.size) return false; + if (snapshotId != volume.snapshotId) return false; + if (virtualMachineId != volume.virtualMachineId) return false; + if (zoneId != volume.zoneId) return false; + if (attached != null ? !attached.equals(volume.attached) : volume.attached != null) return false; + if (!created.equals(volume.created)) return false; + if (diskOfferingDisplayText != null ? !diskOfferingDisplayText.equals(volume.diskOfferingDisplayText) : + volume.diskOfferingDisplayText != null) + return false; + if (diskOfferingName != null ? !diskOfferingName.equals(volume.diskOfferingName) : volume.diskOfferingName != null) + return false; + if (!domain.equals(volume.domain)) return false; + if (hypervisor != null ? !hypervisor.equals(volume.hypervisor) : volume.hypervisor != null) return false; + if (jobStatus != null ? !jobStatus.equals(volume.jobStatus) : volume.jobStatus != null) return false; + if (name != null ? !name.equals(volume.name) : volume.name != null) return false; + if (serviceOfferingDisplayText != null ? !serviceOfferingDisplayText.equals(volume.serviceOfferingDisplayText) : + volume.serviceOfferingDisplayText != null) + return false; + if (serviceOfferingName != null ? !serviceOfferingName.equals(volume.serviceOfferingName) : + volume.serviceOfferingName != null) + return false; + if (state != null ? !state.equals(volume.state) : volume.state != null) return false; + if (storage != null ? !storage.equals(volume.storage) : volume.storage != null) return false; + if (storageType != null ? !storageType.equals(volume.storageType) : volume.storageType != null) return false; + if (type != null ? !type.equals(volume.type) : volume.type != null) return false; + if (vmDisplayName != null ? !vmDisplayName.equals(volume.vmDisplayName) : volume.vmDisplayName != null) + return false; + if (vmName != null ? !vmName.equals(volume.vmName) : volume.vmName != null) return false; + if (vmState != volume.vmState) return false; + if (zoneName != null ? !zoneName.equals(volume.zoneName) : volume.zoneName != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = (int) (id ^ (id >>> 32)); + result = 31 * result + (attached != null ? attached.hashCode() : 0); + result = 31 * result + created.hashCode(); + result = 31 * result + (int) (deviceId ^ (deviceId >>> 32)); + result = 31 * result + (diskOfferingDisplayText != null ? diskOfferingDisplayText.hashCode() : 0); + result = 31 * result + (int) (diskOfferingId ^ (diskOfferingId >>> 32)); + result = 31 * result + (diskOfferingName != null ? diskOfferingName.hashCode() : 0); + result = 31 * result + domain.hashCode(); + result = 31 * result + (int) (domainId ^ (domainId >>> 32)); + result = 31 * result + (hypervisor != null ? hypervisor.hashCode() : 0); + result = 31 * result + (isExtractable ? 1 : 0); + result = 31 * result + (int) (jobId ^ (jobId >>> 32)); + result = 31 * result + (jobStatus != null ? jobStatus.hashCode() : 0); + result = 31 * result + (name != null ? name.hashCode() : 0); + result = 31 * result + (serviceOfferingDisplayText != null ? serviceOfferingDisplayText.hashCode() : 0); + result = 31 * result + (int) (serviceOfferingId ^ (serviceOfferingId >>> 32)); + result = 31 * result + (serviceOfferingName != null ? serviceOfferingName.hashCode() : 0); + result = 31 * result + (int) (size ^ (size >>> 32)); + result = 31 * result + (int) (snapshotId ^ (snapshotId >>> 32)); + result = 31 * result + (state != null ? state.hashCode() : 0); + result = 31 * result + (storage != null ? storage.hashCode() : 0); + result = 31 * result + (storageType != null ? storageType.hashCode() : 0); + result = 31 * result + (type != null ? type.hashCode() : 0); + result = 31 * result + (int) (virtualMachineId ^ (virtualMachineId >>> 32)); + result = 31 * result + (vmDisplayName != null ? vmDisplayName.hashCode() : 0); + result = 31 * result + (vmName != null ? vmName.hashCode() : 0); + result = 31 * result + (vmState != null ? vmState.hashCode() : 0); + result = 31 * result + (int) (zoneId ^ (zoneId >>> 32)); + result = 31 * result + (zoneName != null ? zoneName.hashCode() : 0); + return result; + } +} diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeAsyncClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeAsyncClient.java new file mode 100644 index 0000000000..88da497b16 --- /dev/null +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeAsyncClient.java @@ -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.cloudstack.features; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import java.util.Set; + +import com.google.common.util.concurrent.ListenableFuture; +import org.jclouds.cloudstack.domain.Volume; +import org.jclouds.cloudstack.filters.QuerySigner; +import org.jclouds.cloudstack.options.ListVolumesOptions; +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.functions.ReturnEmptySetOnNotFoundOr404; +import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; +import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404; + +/** + * Provides asynchronous access to cloudstack via their REST API. + *

+ * + * @author Vijay Kiran + * @see org.jclouds.cloudstack.features.VolumeClient + * @see + */ +@RequestFilters(QuerySigner.class) +@QueryParams(keys = "response", values = "json") +public interface VolumeAsyncClient { + /** + * @see VolumeClient#listVolumes(org.jclouds.cloudstack.options.ListVolumesOptions...) + */ + @GET + @QueryParams(keys = "command", values = "listVolumes") + @SelectJson("volume") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) + ListenableFuture> listVolumes(ListVolumesOptions... options); + + + /** + * @see VolumeClient#createVolumeFromDiskOfferingInZone(String, long, long) + */ + @GET + @QueryParams(keys = "command", values = "createVolume") + @SelectJson("volume") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture createVolumeFromDiskOfferingInZone(@QueryParam("name") String name, + @QueryParam("diskofferingid") long diskOfferingId, + @QueryParam("zoneid") long zoneId); + + /** + * @see VolumeClient#createVolumeWithSnapshot(String, long) + */ + @GET + @QueryParams(keys = "command", values = "createVolume") + @SelectJson("volume") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture createVolumeWithSnapshot(@QueryParam("name") String name, + @QueryParam("snapshotid") long diskOfferingId); + + /** + * @see VolumeClient#deleteVolume(long) + */ + @GET + @QueryParams(keys = "command", values = "deleteVolume") + @ExceptionParser(ReturnVoidOnNotFoundOr404.class) + ListenableFuture deleteVolume(@QueryParam("id") long id); + +} diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeClient.java new file mode 100644 index 0000000000..e34208f3d6 --- /dev/null +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeClient.java @@ -0,0 +1,71 @@ +/** + * 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.cloudstack.features; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.jclouds.cloudstack.domain.Volume; +import org.jclouds.cloudstack.options.ListVolumesOptions; +import org.jclouds.concurrent.Timeout; + +/** + * Provides synchronous access to CloudStack Event features. + *

+ * + * @author Vijay Kiran + * @see + */ +@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS) +public interface VolumeClient { + + /** + * Create a volume with given name and diskOfferingId + * + * @param name name of the volume + * @param diskOfferingId the ID of the disk offering. + * @param zoneId the ID of the availability zone + * @return Volume + */ + Volume createVolumeFromDiskOfferingInZone(String name, long diskOfferingId, long zoneId); + + /** + * Create a volume with given name and snapshotId + * + * @param name name of the volume + * @param snapshotId Snapshot id to be used while creating the volume + * @return Volume + */ + Volume createVolumeWithSnapshot(String name, long snapshotId); + + /** + * List volumes + * + * @return volume list or null if not found + */ + Set listVolumes(ListVolumesOptions... options); + + /** + * Deletes a attached disk volume + * + * @param id id of the volume + */ + void deleteVolume(long id); + +} diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListVolumesOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListVolumesOptions.java new file mode 100644 index 0000000000..f875f5a74e --- /dev/null +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListVolumesOptions.java @@ -0,0 +1,218 @@ +/** + * 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.cloudstack.options; + +import com.google.common.collect.ImmutableSet; + +/** + * Options used to control what volume + * + * @author Vijay Kiran + */ +public class ListVolumesOptions extends AccountInDomainOptions { + + public static final ListVolumesOptions NONE = new ListVolumesOptions(); + + /** + * List volumes on specified host + * + * @param hostId hostId. + */ + public ListVolumesOptions hostId(long hostId) { + this.queryParameters.replaceValues("hostid", ImmutableSet.of(hostId + "")); + return this; + } + + /** + * @param id the ID of the volume + */ + public ListVolumesOptions id(long id) { + this.queryParameters.replaceValues("id", ImmutableSet.of(id + "")); + return this; + } + + /** + * @param recursive defaults to false, but if true, lists all volumes from the parent specified by the domain id till leaves. + */ + public ListVolumesOptions isRecursive(boolean recursive) { + this.queryParameters.replaceValues("isrecursive", ImmutableSet.of(String.valueOf(recursive))); + return this; + } + + + /** + * @param keyword List by keyword + */ + public ListVolumesOptions keyword(String keyword) { + this.queryParameters.replaceValues("keyword", ImmutableSet.of(keyword)); + return this; + } + + /** + * @param name the name of the disk volume + */ + public ListVolumesOptions name(String name) { + this.queryParameters.replaceValues("name", ImmutableSet.of(name)); + return this; + } + + /** + * @param podId the pod id the disk volume belongs to + */ + public ListVolumesOptions podId(long podId) { + this.queryParameters.replaceValues("podid", ImmutableSet.of(podId + "")); + return this; + + } + + /** + * @param type the type of the disk volume + */ + public ListVolumesOptions type(String type) { + this.queryParameters.replaceValues("type", ImmutableSet.of(type + "")); + return this; + } + + /** + * @param virtualMachineId list volumes by id virtual machine. + */ + public ListVolumesOptions virtualMachineId(long virtualMachineId) { + this.queryParameters.replaceValues("virtualmachineid", ImmutableSet.of(virtualMachineId + "")); + return this; + + } + + /** + * @param zoneId list volumes by zoneId. + */ + public ListVolumesOptions zoneId(long zoneId) { + this.queryParameters.replaceValues("zoneid", ImmutableSet.of(zoneId + "")); + return this; + + } + + + public static class Builder { + + /** + * @see ListVolumesOptions#accountInDomain + */ + public static ListVolumesOptions accountInDomain(String account, long domain) { + ListVolumesOptions options = new ListVolumesOptions(); + return options.accountInDomain(account, domain); + } + + /** + * @see ListVolumesOptions#domainId + */ + public static ListVolumesOptions domainId(long id) { + ListVolumesOptions options = new ListVolumesOptions(); + return options.domainId(id); + } + + /** + * @see ListVolumesOptions#hostId + */ + public static ListVolumesOptions hostId(long id) { + ListVolumesOptions options = new ListVolumesOptions(); + return options.hostId(id); + } + + /** + * @see ListVolumesOptions#id + */ + public static ListVolumesOptions id(long id) { + ListVolumesOptions options = new ListVolumesOptions(); + return options.id(id); + } + + /** + * @see ListVolumesOptions#isRecursive(boolean) + */ + public static ListVolumesOptions isRecursive(boolean recursive) { + ListVolumesOptions options = new ListVolumesOptions(); + return isRecursive(recursive); + } + + /** + * @see ListVolumesOptions#name + */ + public static ListVolumesOptions keyword(String keyword) { + ListVolumesOptions options = new ListVolumesOptions(); + return options.keyword(keyword); + } + + /** + * @see ListVolumesOptions#name + */ + public static ListVolumesOptions name(String name) { + ListVolumesOptions options = new ListVolumesOptions(); + return options.name(name); + } + + + /** + * @see ListVolumesOptions#podId + */ + public static ListVolumesOptions podId(long id) { + ListVolumesOptions options = new ListVolumesOptions(); + return options.podId(id); + } + + /** + * @see ListVolumesOptions#type + */ + public static ListVolumesOptions type(String type) { + ListVolumesOptions options = new ListVolumesOptions(); + return options.type(type); + } + + /** + * @see ListVolumesOptions#virtualMachineId(long) + */ + public static ListVolumesOptions virtualMachineId(long virtualMachineId) { + ListVolumesOptions options = new ListVolumesOptions(); + return options.virtualMachineId(virtualMachineId); + } + + /** + * @see ListVolumesOptions#zoneId + */ + public static ListVolumesOptions zoneId(long id) { + ListVolumesOptions options = new ListVolumesOptions(); + return options.zoneId(id); + } + } + + /** + * {@inheritDoc} + */ + @Override + public ListVolumesOptions accountInDomain(String account, long domain) { + return ListVolumesOptions.class.cast(super.accountInDomain(account, domain)); + } + + /** + * {@inheritDoc} + */ + @Override + public ListVolumesOptions domainId(long domainId) { + return ListVolumesOptions.class.cast(super.domainId(domainId)); + } +} diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeAsyncClientTest.java new file mode 100644 index 0000000000..d88bfb9595 --- /dev/null +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeAsyncClientTest.java @@ -0,0 +1,117 @@ +/** + * 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.cloudstack.features; + +import java.io.IOException; +import java.lang.reflect.Method; + +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; + +/** + * Tests behavior of {@code EventAsyncClient} + * + * @author Vijay Kiran + */ +// NOTE:without testName, this will not call @Before* and fail w/NPE during +// surefire +@Test(groups = "unit", testName = "VolumeAsyncClientTest") +public class VolumeAsyncClientTest extends BaseCloudStackAsyncClientTest { + + protected String prefix = System.getProperty("user.name"); + + public void testListVolumes() throws SecurityException, NoSuchMethodException, IOException { + Method method = VolumeAsyncClient.class.getMethod("listVolumes", ListVolumesOptions[].class); + HttpRequest httpRequest = processor.createRequest(method); + + assertRequestLineEquals(httpRequest, + "GET http://localhost:8080/client/api?response=json&command=listVolumes HTTP/1.1"); + assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n"); + assertPayloadEquals(httpRequest, null, null, false); + + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class); + + checkFilters(httpRequest); + + } + + public void testCreateVolumeWithSnapshot() throws SecurityException, NoSuchMethodException, IOException { + 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"); + assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n"); + assertPayloadEquals(httpRequest, null, null, false); + + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class); + checkFilters(httpRequest); + + } + + public void testCreateVolumeWithDiskOffering() throws SecurityException, NoSuchMethodException, IOException { + Method method = VolumeAsyncClient.class.getMethod("createVolumeWithDiskOfferingInZone", + 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"); + 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); + 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"); + assertPayloadEquals(httpRequest, null, null, false); + + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, ReturnVoidOnNotFoundOr404.class); + checkFilters(httpRequest); + + } + + + + + @Override + protected TypeLiteral> createTypeLiteral() { + return new TypeLiteral>() { + }; + } +} diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeClientLiveTest.java new file mode 100644 index 0000000000..eae7c0a70a --- /dev/null +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeClientLiveTest.java @@ -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.cloudstack.features; + +import static org.testng.AssertJUnit.assertNotNull; + +import java.util.Set; + +import org.jclouds.cloudstack.domain.DiskOffering; +import org.jclouds.cloudstack.domain.Volume; +import org.jclouds.cloudstack.domain.Zone; +import org.testng.annotations.Test; + +/** + * Tests behavior of {@code SSHKeyPairClient} + * + * @author Vijay Kiran + */ +@Test(groups = "live", singleThreaded = true, testName = "VolumeClientLiveTest") +public class VolumeClientLiveTest extends BaseCloudStackClientLiveTest { + + protected String prefix = System.getProperty("user.name"); + + public void testListVolumes() { + final Set volumes = client.getVolumeClient().listVolumes(); + for (Volume volume : volumes) { + checkVolume(volume); + } + } + + public void testCreateVolumeFromDiskofferingInZoneAndDeleteVolume() { + + final Set zones = client.getZoneClient().listZones(); + assertNotNull(zones); + final Zone zone = zones.iterator().next(); + + final Set diskOfferings = client.getOfferingClient().listDiskOfferings(); + assertNotNull(diskOfferings); + + //Pick some disk offering + final DiskOffering diskOffering = diskOfferings.iterator().next(); + final VolumeClient volumeClient = client.getVolumeClient(); + + final Volume volumeWithDiskOffering = + volumeClient.createVolumeFromDiskOfferingInZone(prefix + "-jclouds-volume", + diskOffering.getId(), + zone.getId()); + checkVolume(volumeWithDiskOffering); + volumeClient.deleteVolume(volumeWithDiskOffering.getId()); + } + + + private void checkVolume(Volume volume) { + assertNotNull(volume.getId()); + assertNotNull(volume.getName()); + } +} From e6090530be0ca8ab585da30ab122557a90f4787c Mon Sep 17 00:00:00 2001 From: vijaykiran Date: Mon, 14 Nov 2011 21:57:01 +0100 Subject: [PATCH 2/3] volume-client create volume test --- .../cloudstack/CloudStackAsyncClient.java | 7 +++ .../jclouds/cloudstack/CloudStackClient.java | 7 +++ .../config/CloudStackRestClientModule.java | 3 ++ .../features/VolumeAsyncClient.java | 11 ++-- .../cloudstack/features/VolumeClient.java | 5 +- .../cloudstack/CloudStackAsyncClientTest.java | 2 + .../features/VolumeAsyncClientTest.java | 7 +-- .../features/VolumeClientLiveTest.java | 54 +++++++++++++------ 8 files changed, 69 insertions(+), 27 deletions(-) diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java index 1fb1723adb..6f5d80d0e0 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java @@ -37,6 +37,7 @@ import org.jclouds.cloudstack.features.SecurityGroupAsyncClient; import org.jclouds.cloudstack.features.TemplateAsyncClient; import org.jclouds.cloudstack.features.VMGroupAsyncClient; import org.jclouds.cloudstack.features.VirtualMachineAsyncClient; +import org.jclouds.cloudstack.features.VolumeAsyncClient; import org.jclouds.cloudstack.features.ZoneAsyncClient; import org.jclouds.rest.annotations.Delegate; @@ -170,4 +171,10 @@ public interface CloudStackAsyncClient { */ @Delegate ISOAsyncClient getISOClient(); + + /** + * Provides asynchronous access to Volumes + */ + @Delegate + VolumeAsyncClient getVolumeClient(); } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java index 8ba40e5d8a..760f36cff8 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java @@ -39,6 +39,7 @@ import org.jclouds.cloudstack.features.SecurityGroupClient; import org.jclouds.cloudstack.features.TemplateClient; import org.jclouds.cloudstack.features.VMGroupClient; import org.jclouds.cloudstack.features.VirtualMachineClient; +import org.jclouds.cloudstack.features.VolumeClient; import org.jclouds.cloudstack.features.ZoneClient; import org.jclouds.concurrent.Timeout; import org.jclouds.rest.annotations.Delegate; @@ -173,4 +174,10 @@ public interface CloudStackClient { */ @Delegate ISOClient getISOClient(); + + /** + * Provides synchronous access to Volumes + */ + @Delegate + VolumeClient getVolumeClient(); } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java index df677b311b..8e45950aca 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java @@ -61,6 +61,8 @@ import org.jclouds.cloudstack.features.VMGroupAsyncClient; import org.jclouds.cloudstack.features.VMGroupClient; import org.jclouds.cloudstack.features.VirtualMachineAsyncClient; import org.jclouds.cloudstack.features.VirtualMachineClient; +import org.jclouds.cloudstack.features.VolumeAsyncClient; +import org.jclouds.cloudstack.features.VolumeClient; import org.jclouds.cloudstack.features.ZoneAsyncClient; import org.jclouds.cloudstack.features.ZoneClient; import org.jclouds.cloudstack.handlers.CloudStackErrorHandler; @@ -104,6 +106,7 @@ public class CloudStackRestClientModule extends RestClientModule createVolumeFromDiskOfferingInZone(@QueryParam("name") String name, - @QueryParam("diskofferingid") long diskOfferingId, - @QueryParam("zoneid") long zoneId); + ListenableFuture createVolumeFromDiskOfferingInZone(@QueryParam("name") String name, + @QueryParam("diskofferingid") long diskOfferingId, + @QueryParam("zoneid") long zoneId); /** * @see VolumeClient#createVolumeWithSnapshot(String, long) @@ -78,8 +79,8 @@ public interface VolumeAsyncClient { @SelectJson("volume") @Consumes(MediaType.APPLICATION_JSON) @ExceptionParser(ReturnNullOnNotFoundOr404.class) - ListenableFuture createVolumeWithSnapshot(@QueryParam("name") String name, - @QueryParam("snapshotid") long diskOfferingId); + ListenableFuture createVolumeWithSnapshot(@QueryParam("name") String name, + @QueryParam("snapshotid") long diskOfferingId); /** * @see VolumeClient#deleteVolume(long) diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeClient.java index e34208f3d6..960751afd4 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeClient.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeClient.java @@ -21,6 +21,7 @@ package org.jclouds.cloudstack.features; import java.util.Set; import java.util.concurrent.TimeUnit; +import org.jclouds.cloudstack.domain.AsyncCreateResponse; import org.jclouds.cloudstack.domain.Volume; import org.jclouds.cloudstack.options.ListVolumesOptions; import org.jclouds.concurrent.Timeout; @@ -43,7 +44,7 @@ public interface VolumeClient { * @param zoneId the ID of the availability zone * @return Volume */ - Volume createVolumeFromDiskOfferingInZone(String name, long diskOfferingId, long zoneId); + AsyncCreateResponse createVolumeFromDiskOfferingInZone(String name, long diskOfferingId, long zoneId); /** * Create a volume with given name and snapshotId @@ -52,7 +53,7 @@ public interface VolumeClient { * @param snapshotId Snapshot id to be used while creating the volume * @return Volume */ - Volume createVolumeWithSnapshot(String name, long snapshotId); + AsyncCreateResponse createVolumeWithSnapshot(String name, long snapshotId); /** * List volumes diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/CloudStackAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/CloudStackAsyncClientTest.java index e7db88988c..7042300de1 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/CloudStackAsyncClientTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/CloudStackAsyncClientTest.java @@ -62,6 +62,7 @@ public class CloudStackAsyncClientTest extends BaseCloudStackAsyncClientTest volumes = client.getVolumeClient().listVolumes(); @@ -46,23 +51,25 @@ public class VolumeClientLiveTest extends BaseCloudStackClientLiveTest { public void testCreateVolumeFromDiskofferingInZoneAndDeleteVolume() { - final Set zones = client.getZoneClient().listZones(); - assertNotNull(zones); - final Zone zone = zones.iterator().next(); - - final Set diskOfferings = client.getOfferingClient().listDiskOfferings(); - assertNotNull(diskOfferings); - + zoneId = Iterables.getFirst(client.getZoneClient().listZones(), null).getId(); //Pick some disk offering - final DiskOffering diskOffering = diskOfferings.iterator().next(); - final VolumeClient volumeClient = client.getVolumeClient(); + 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 Retry ? + e.printStackTrace(); + } + } + + checkVolume(volume); - final Volume volumeWithDiskOffering = - volumeClient.createVolumeFromDiskOfferingInZone(prefix + "-jclouds-volume", - diskOffering.getId(), - zone.getId()); - checkVolume(volumeWithDiskOffering); - volumeClient.deleteVolume(volumeWithDiskOffering.getId()); } @@ -70,4 +77,17 @@ public class VolumeClientLiveTest extends BaseCloudStackClientLiveTest { assertNotNull(volume.getId()); assertNotNull(volume.getName()); } + + private Volume findVolumeWithId(final long id) { + System.out.println(id); + return find(client.getVolumeClient().listVolumes(), new Predicate() { + + @Override + public boolean apply(Volume arg0) { + return arg0.getId() == id; + } + + }); + } + } From 5e276964810c9032f67a57ae4821e51050a2e1cd Mon Sep 17 00:00:00 2001 From: vijaykiran Date: Tue, 15 Nov 2011 12:14:30 +0100 Subject: [PATCH 3/3] VolumeClient Implementation - Attach/Detach/CreateFromSnapshot and CreatefromVolume with parrial tests. --- .../cloudstack/CloudStackAsyncClient.java | 7 ++ .../jclouds/cloudstack/CloudStackClient.java | 9 +- .../config/CloudStackRestClientModule.java | 3 + .../org/jclouds/cloudstack/domain/Volume.java | 54 ++++++++++-- .../features/VolumeAsyncClient.java | 34 +++++-- .../cloudstack/features/VolumeClient.java | 26 +++++- .../cloudstack/CloudStackAsyncClientTest.java | 2 + .../features/VolumeAsyncClientTest.java | 49 ++++++++--- .../features/VolumeClientLiveTest.java | 88 ++++++++++++++++++- 9 files changed, 236 insertions(+), 36 deletions(-) diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java index 6f5d80d0e0..baf433b5e1 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java @@ -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(); } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java index 760f36cff8..82741a32aa 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java @@ -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; @@ -175,9 +176,15 @@ public interface CloudStackClient { @Delegate ISOClient getISOClient(); - /** + /** * Provides synchronous access to Volumes */ @Delegate VolumeClient getVolumeClient(); + + /** + * Provides synchronous access to Snapshots + */ + @Delegate + SnapshotClient getSnapshotClient(); } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java index 8e45950aca..0211c1757a 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java @@ -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 { 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 { 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 { 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 { 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 { return storageType; } - public String getType() { + public VolumeType getType() { return type; } @@ -530,4 +534,38 @@ public class Volume implements Comparable { 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 INDEX = Maps.uniqueIndex(ImmutableSet.copyOf(VolumeType.values()), + new Function() { + + @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; + } + + } + } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeAsyncClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeAsyncClient.java index 7b2e1dd7cd..28271b08f6 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeAsyncClient.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeAsyncClient.java @@ -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 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 createVolumeWithSnapshot(@QueryParam("name") String name, - @QueryParam("snapshotid") long diskOfferingId); + ListenableFuture 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 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 detachVolume(@QueryParam("id") long volumeId); /** * @see VolumeClient#deleteVolume(long) diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeClient.java index 960751afd4..2a71a3b605 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeClient.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/VolumeClient.java @@ -41,8 +41,8 @@ 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 + * @param zoneId the ID of the availability zone + * @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); + } diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/CloudStackAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/CloudStackAsyncClientTest.java index 7042300de1..f622c029a9 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/CloudStackAsyncClientTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/CloudStackAsyncClientTest.java @@ -63,6 +63,7 @@ public class CloudStackAsyncClientTest extends BaseCloudStackAsyncClientTest> createTypeLiteral() { return new TypeLiteral>() { diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeClientLiveTest.java index bed106c2a0..30ff3113db 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeClientLiveTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeClientLiveTest.java @@ -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 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() { @Override