Issue 29: added attach/detach

git-svn-id: http://jclouds.googlecode.com/svn/trunk@2532 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2009-12-28 23:17:00 +00:00
parent c5d67c7cb3
commit 719aa3483b
14 changed files with 688 additions and 119 deletions

View File

@ -0,0 +1,144 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.aws.ec2.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Date;
/**
*
* @see <a href=
* "http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-CreateVolume.html"
* />
* @author Adrian Cole
*/
public class Attachment {
public static enum Status {
ATTACHING, ATTACHED, DETACHING, DETACHED;
public String value() {
return name().toLowerCase();
}
@Override
public String toString() {
return value();
}
public static Status fromValue(String status) {
return valueOf(checkNotNull(status, "status").toUpperCase());
}
}
private final String volumeId;
private final String instanceId;
private final String device;
private final Status status;
private final Date attachTime;
public Attachment(String volumeId, String instanceId, String device, Status status,
Date attachTime) {
this.volumeId = volumeId;
this.instanceId = instanceId;
this.device = device;
this.status = status;
this.attachTime = attachTime;
}
public String getVolumeId() {
return volumeId;
}
public String getInstanceId() {
return instanceId;
}
public String getDevice() {
return device;
}
public Status getStatus() {
return status;
}
public Date getAttachTime() {
return attachTime;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((attachTime == null) ? 0 : attachTime.hashCode());
result = prime * result + ((device == null) ? 0 : device.hashCode());
result = prime * result + ((instanceId == null) ? 0 : instanceId.hashCode());
result = prime * result + ((status == null) ? 0 : status.hashCode());
result = prime * result + ((volumeId == null) ? 0 : volumeId.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Attachment other = (Attachment) obj;
if (attachTime == null) {
if (other.attachTime != null)
return false;
} else if (!attachTime.equals(other.attachTime))
return false;
if (device == null) {
if (other.device != null)
return false;
} else if (!device.equals(other.device))
return false;
if (instanceId == null) {
if (other.instanceId != null)
return false;
} else if (!instanceId.equals(other.instanceId))
return false;
if (status == null) {
if (other.status != null)
return false;
} else if (!status.equals(other.status))
return false;
if (volumeId == null) {
if (other.volumeId != null)
return false;
} else if (!volumeId.equals(other.volumeId))
return false;
return true;
}
@Override
public String toString() {
return "Attachment [attachTime=" + attachTime + ", device=" + device + ", instanceId="
+ instanceId + ", status=" + status + ", volumeId=" + volumeId + "]";
}
}

View File

@ -60,115 +60,6 @@ public class Volume implements Comparable<Volume> {
} }
} }
public static class Attachment {
public static enum Status {
ATTACHING, ATTACHED, DETACHING, DETACHED;
public String value() {
return name().toLowerCase();
}
@Override
public String toString() {
return value();
}
public static Status fromValue(String status) {
return valueOf(checkNotNull(status, "status").toUpperCase());
}
}
private final String volumeId;
private final String instanceId;
private final String device;
private final Status status;
private final Date attachTime;
public Attachment(String volumeId, String instanceId, String device, Status status,
Date attachTime) {
this.volumeId = volumeId;
this.instanceId = instanceId;
this.device = device;
this.status = status;
this.attachTime = attachTime;
}
public String getVolumeId() {
return volumeId;
}
public String getInstanceId() {
return instanceId;
}
public String getDevice() {
return device;
}
public Status getStatus() {
return status;
}
public Date getAttachTime() {
return attachTime;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((attachTime == null) ? 0 : attachTime.hashCode());
result = prime * result + ((device == null) ? 0 : device.hashCode());
result = prime * result + ((instanceId == null) ? 0 : instanceId.hashCode());
result = prime * result + ((status == null) ? 0 : status.hashCode());
result = prime * result + ((volumeId == null) ? 0 : volumeId.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Attachment other = (Attachment) obj;
if (attachTime == null) {
if (other.attachTime != null)
return false;
} else if (!attachTime.equals(other.attachTime))
return false;
if (device == null) {
if (other.device != null)
return false;
} else if (!device.equals(other.device))
return false;
if (instanceId == null) {
if (other.instanceId != null)
return false;
} else if (!instanceId.equals(other.instanceId))
return false;
if (status == null) {
if (other.status != null)
return false;
} else if (!status.equals(other.status))
return false;
if (volumeId == null) {
if (other.volumeId != null)
return false;
} else if (!volumeId.equals(other.volumeId))
return false;
return true;
}
@Override
public String toString() {
return "Attachment [attachTime=" + attachTime + ", device=" + device + ", instanceId="
+ instanceId + ", status=" + status + ", volumeId=" + volumeId + "]";
}
}
private final String id; private final String id;
private final int size; private final int size;
@Nullable @Nullable

View File

@ -0,0 +1,118 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed from this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* from 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.aws.ec2.options;
import static com.google.common.base.Preconditions.checkNotNull;
import org.jclouds.aws.ec2.options.internal.BaseEC2RequestOptions;
/**
* Contains options supported in the Form API for the DetachVolume operation. <h2>
* Usage</h2> The recommended way to instantiate a DetachVolumeOptions object is to statically
* import DetachVolumeOptions.Builder.* and invoke a static creation method followed by an instance
* mutator (if needed):
* <p/>
* <code>
* import static org.jclouds.aws.ec2.options.DetachVolumeOptions.Builder.*
* <p/>
* EC2Client client = // get connection
* client.getElasticBlockStoreServices().detachVolumeInRegion(Region.DEFAULT, id, fromDevice("123125").force());
* <code>
*
* @author Adrian Cole
* @see <a
* href="http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-form-DetachVolume.html"
* />
*/
public class DetachVolumeOptions extends BaseEC2RequestOptions {
/**
* The ID of the instance.
*/
public DetachVolumeOptions fromInstance(String instanceId) {
formParameters.put("InstanceId", checkNotNull(instanceId, "instanceId"));
return this;
}
public String getInstance() {
return getFirstFormOrNull("InstanceId");
}
/**
* The device name.
*/
public DetachVolumeOptions fromDevice(String device) {
formParameters.put("Device", checkNotNull(device, "device"));
return this;
}
public String getDevice() {
return getFirstFormOrNull("Device");
}
/**
* Forces detachment if the previous detachment attempt did not occur cleanly (logging into an
* instance, unmounting the volume, and detaching normally). This option can lead to data loss or
* a corrupted file system. Use this option only as a last resort to detach a volume from a
* failed instance. The instance will not have an opportunity to flush file system caches nor
* file system meta data. If you use this option, you must perform file system check and repair
* procedures.
*/
public DetachVolumeOptions force() {
formParameters.put("Force", "true");
return this;
}
public boolean getForce() {
return getFirstFormOrNull("Force") != null;
}
public static class Builder {
/**
* @see DetachVolumeOptions#fromInstance(String )
*/
public static DetachVolumeOptions fromInstance(String instance) {
DetachVolumeOptions options = new DetachVolumeOptions();
return options.fromInstance(instance);
}
/**
* @see DetachVolumeOptions#fromDevice(String )
*/
public static DetachVolumeOptions fromDevice(String device) {
DetachVolumeOptions options = new DetachVolumeOptions();
return options.fromDevice(device);
}
/**
* @see DetachVolumeOptions#force()
*/
public static DetachVolumeOptions force() {
DetachVolumeOptions options = new DetachVolumeOptions();
return options.force();
}
}
}

View File

@ -35,11 +35,14 @@ import javax.ws.rs.Path;
import org.jclouds.aws.ec2.EC2; import org.jclouds.aws.ec2.EC2;
import org.jclouds.aws.ec2.binders.BindVolumeIdsToIndexedFormParams; import org.jclouds.aws.ec2.binders.BindVolumeIdsToIndexedFormParams;
import org.jclouds.aws.ec2.domain.Attachment;
import org.jclouds.aws.ec2.domain.AvailabilityZone; import org.jclouds.aws.ec2.domain.AvailabilityZone;
import org.jclouds.aws.ec2.domain.Region; import org.jclouds.aws.ec2.domain.Region;
import org.jclouds.aws.ec2.domain.Volume; import org.jclouds.aws.ec2.domain.Volume;
import org.jclouds.aws.ec2.filters.FormSigner; import org.jclouds.aws.ec2.filters.FormSigner;
import org.jclouds.aws.ec2.functions.RegionToEndpoint; import org.jclouds.aws.ec2.functions.RegionToEndpoint;
import org.jclouds.aws.ec2.options.DetachVolumeOptions;
import org.jclouds.aws.ec2.xml.AttachmentHandler;
import org.jclouds.aws.ec2.xml.CreateVolumeResponseHandler; import org.jclouds.aws.ec2.xml.CreateVolumeResponseHandler;
import org.jclouds.aws.ec2.xml.DescribeVolumesResponseHandler; import org.jclouds.aws.ec2.xml.DescribeVolumesResponseHandler;
import org.jclouds.rest.annotations.BinderParam; import org.jclouds.rest.annotations.BinderParam;
@ -107,4 +110,27 @@ public interface ElasticBlockStoreAsyncClient {
Future<Void> deleteVolumeInRegion(@EndpointParam(parser = RegionToEndpoint.class) Region region, Future<Void> deleteVolumeInRegion(@EndpointParam(parser = RegionToEndpoint.class) Region region,
@FormParam("VolumeId") String volumeId); @FormParam("VolumeId") String volumeId);
/**
* @see ElasticBlockStoreClient#detachVolumeInRegion
*/
@POST
@Path("/")
@FormParams(keys = ACTION, values = "DetachVolume")
@XMLResponseParser(AttachmentHandler.class)
Future<Attachment> detachVolumeInRegion(
@EndpointParam(parser = RegionToEndpoint.class) Region region,
@FormParam("VolumeId") String volumeId, DetachVolumeOptions... options);
/**
* @see ElasticBlockStoreClient#attachVolumeInRegion
*/
@POST
@Path("/")
@FormParams(keys = ACTION, values = "AttachVolume")
@XMLResponseParser(AttachmentHandler.class)
Future<Attachment> attachVolumeInRegion(
@EndpointParam(parser = RegionToEndpoint.class) Region region,
@FormParam("VolumeId") String volumeId, @FormParam("InstanceId") String instanceId,
@FormParam("Device") String device);
} }

View File

@ -26,9 +26,11 @@ package org.jclouds.aws.ec2.services;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.jclouds.aws.ec2.domain.Attachment;
import org.jclouds.aws.ec2.domain.AvailabilityZone; import org.jclouds.aws.ec2.domain.AvailabilityZone;
import org.jclouds.aws.ec2.domain.Region; import org.jclouds.aws.ec2.domain.Region;
import org.jclouds.aws.ec2.domain.Volume; import org.jclouds.aws.ec2.domain.Volume;
import org.jclouds.aws.ec2.options.DetachVolumeOptions;
import org.jclouds.concurrent.Timeout; import org.jclouds.concurrent.Timeout;
/** /**
@ -117,10 +119,10 @@ public interface ElasticBlockStoreClient {
* The ID of the volume to delete. The volume remains in the deleting state for several * The ID of the volume to delete. The volume remains in the deleting state for several
* minutes after entering this command. * minutes after entering this command.
* *
* @see #describeVolumes * @see #describeVolumesInRegion
* @see #createVolume * @see #createVolumeInRegion
* @see #attachVolume * @see #attachVolumeInRegion
* @see #detachVolume * @see #detachVolumeInRegion
* *
* @see <a href= * @see <a href=
* "http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DeleteVolume.html" * "http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DeleteVolume.html"
@ -128,4 +130,67 @@ public interface ElasticBlockStoreClient {
*/ */
void deleteVolumeInRegion(Region region, String volumeId); void deleteVolumeInRegion(Region region, String volumeId);
/**
* Attaches an Amazon EBS volume to a running instance and exposes it as the specified device.
* <p/>
*
* <h3>Note</h3>
*
* Windows instances currently support devices xvda through xvdp. Devices xvda and xvdb are
* reserved by the operating system, xvdc is assigned to drive C:\, and, depending on the
* instance type, devices xvdd through xvde might be reserved by the instance stores. Any device
* that is not reserved can be attached to an Amazon EBS volume. For a list of devices that are
* reserved by the instance stores, go to the Amazon Elastic Compute Cloud Developer Guide or
* Amazon Elastic Compute Cloud User Guide.
*
* @param region
* region where the volume is defined
* @param volumeId
* The ID of the volume to delete. The volume remains in the deleting state for several
* minutes after entering this command.
* @param options
* options like force()
*
* @see #describeVolumesInRegion
* @see #createVolumeInRegion
* @see #attachVolumeInRegion
* @see #deleteVolumeInRegion
*
* @see <a href=
* "http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DetachVolume.html"
* />
*/
Attachment detachVolumeInRegion(Region region, String volumeId, DetachVolumeOptions... options);
/**
* Attaches an Amazon EBS volume to a running instance and exposes it as the specified device.
*
* <h3>Note</h3> Windows instances currently support devices xvda through xvdp. Devices xvda and
* xvdb are reserved by the operating system, xvdc is assigned to drive C:\, and, depending on
* the instance type, devices xvdd through xvde might be reserved by the instance stores. Any
* device that is not reserved can be attached to an Amazon EBS volume. For a list of devices
* that are reserved by the instance stores, go to the Amazon Elastic Compute Cloud Developer
* Guide or Amazon Elastic Compute Cloud User Guide.
*
* @param region
* region where the volume is defined
* @param volumeId
* The ID of the Amazon EBS volume. The volume and instance must be within the same
* Availability Zone and the instance must be running.
* @param instanceId
* The ID of the instance to which the volume attaches. The volume and instance must be
* within the same Availability Zone and the instance must be running.
* @param device
* Specifies how the device is exposed to the instance (e.g., /dev/sdh).
*
* @see #describeVolumesInRegion
* @see #createVolumeInRegion
* @see #detachVolumeInRegion
* @see #deleteVolumeInRegion
*
* @see <a href=
* "http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-AttachVolume.html"
* />
*/
Attachment attachVolumeInRegion(Region region, String volumeId, String instanceId, String device);
} }

View File

@ -0,0 +1,80 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.aws.ec2.xml;
import java.util.Date;
import javax.annotation.Resource;
import javax.inject.Inject;
import org.jclouds.aws.ec2.domain.Attachment;
import org.jclouds.date.DateService;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.logging.Logger;
/**
*
* @author Adrian Cole
*/
public class AttachmentHandler extends ParseSax.HandlerWithResult<Attachment> {
private StringBuilder currentText = new StringBuilder();
@Resource
protected Logger logger = Logger.NULL;
@Inject
protected DateService dateService;
private String volumeId;
private String instanceId;
private String device;
private Attachment.Status attachmentStatus;
private Date attachTime;
public Attachment getResult() {
return new Attachment(volumeId, instanceId, device, attachmentStatus, attachTime);
}
public void endElement(String uri, String name, String qName) {
if (qName.equals("volumeId")) {
volumeId = currentText.toString().trim();
} else if (qName.equals("volumeId")) {
volumeId = currentText.toString().trim();
} else if (qName.equals("status")) {
attachmentStatus = Attachment.Status.fromValue(currentText.toString().trim());
} else if (qName.equals("instanceId")) {
instanceId = currentText.toString().trim();
} else if (qName.equals("device")) {
device = currentText.toString().trim();
} else if (qName.equals("attachTime")) {
attachTime = dateService.iso8601DateParse(currentText.toString().trim());
}
currentText = new StringBuilder();
}
public void characters(char ch[], int start, int length) {
currentText.append(ch, start, length);
}
}

View File

@ -29,9 +29,9 @@ import java.util.Set;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.inject.Inject; import javax.inject.Inject;
import org.jclouds.aws.ec2.domain.Attachment;
import org.jclouds.aws.ec2.domain.AvailabilityZone; import org.jclouds.aws.ec2.domain.AvailabilityZone;
import org.jclouds.aws.ec2.domain.Volume; import org.jclouds.aws.ec2.domain.Volume;
import org.jclouds.aws.ec2.domain.Volume.Attachment;
import org.jclouds.date.DateService; import org.jclouds.date.DateService;
import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ParseSax;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
@ -62,7 +62,7 @@ public class CreateVolumeResponseHandler extends ParseSax.HandlerWithResult<Volu
private String volumeId; private String volumeId;
private String instanceId; private String instanceId;
private String device; private String device;
private Volume.Attachment.Status attachmentStatus; private Attachment.Status attachmentStatus;
private Date attachTime; private Date attachTime;
private boolean inAttachmentSet; private boolean inAttachmentSet;
@ -102,7 +102,7 @@ public class CreateVolumeResponseHandler extends ParseSax.HandlerWithResult<Volu
} }
} else if (qName.equals("status")) { } else if (qName.equals("status")) {
if (inAttachmentSet) { if (inAttachmentSet) {
attachmentStatus = Volume.Attachment.Status.fromValue(currentText.toString().trim()); attachmentStatus = Attachment.Status.fromValue(currentText.toString().trim());
} else { } else {
volumeStatus = Volume.Status.fromValue(currentText.toString().trim()); volumeStatus = Volume.Status.fromValue(currentText.toString().trim());
} }

View File

@ -0,0 +1,112 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.aws.ec2.options;
import static org.jclouds.aws.ec2.options.DetachVolumeOptions.Builder.force;
import static org.jclouds.aws.ec2.options.DetachVolumeOptions.Builder.fromDevice;
import static org.jclouds.aws.ec2.options.DetachVolumeOptions.Builder.fromInstance;
import static org.testng.Assert.assertEquals;
import java.util.Collections;
import org.jclouds.http.options.HttpRequestOptions;
import org.testng.annotations.Test;
/**
* Tests possible uses of DetachVolumeOptions and DetachVolumeOptions.Builder.*
*
* @author Adrian Cole
*/
public class DetachVolumeOptionsTest {
@Test
public void testAssignability() {
assert HttpRequestOptions.class.isAssignableFrom(DetachVolumeOptions.class);
assert !String.class.isAssignableFrom(DetachVolumeOptions.class);
}
@Test
public void testFromDevice() {
DetachVolumeOptions options = new DetachVolumeOptions();
options.fromDevice("test");
assertEquals(options.buildFormParameters().get("Device"), Collections.singletonList("test"));
}
@Test
public void testNullFromDevice() {
DetachVolumeOptions options = new DetachVolumeOptions();
assertEquals(options.buildFormParameters().get("Device"), Collections.EMPTY_LIST);
}
@Test
public void testFromDeviceStatic() {
DetachVolumeOptions options = fromDevice("test");
assertEquals(options.buildFormParameters().get("Device"), Collections.singletonList("test"));
}
@Test(expectedExceptions = NullPointerException.class)
public void testFromDeviceNPE() {
fromDevice(null);
}
@Test
public void testFromInstance() {
DetachVolumeOptions options = new DetachVolumeOptions();
options.fromInstance("test");
assertEquals(options.buildFormParameters().get("InstanceId"), Collections
.singletonList("test"));
}
@Test
public void testNullFromInstance() {
DetachVolumeOptions options = new DetachVolumeOptions();
assertEquals(options.buildFormParameters().get("InstanceId"), Collections.EMPTY_LIST);
}
@Test
public void testFromInstanceStatic() {
DetachVolumeOptions options = fromInstance("test");
assertEquals(options.buildFormParameters().get("InstanceId"), Collections
.singletonList("test"));
}
@Test(expectedExceptions = NullPointerException.class)
public void testFromInstanceNPE() {
fromInstance(null);
}
@Test
public void testForce() {
DetachVolumeOptions options = new DetachVolumeOptions();
options.force();
assertEquals(options.buildFormParameters().get("Force"), Collections.singletonList("true"));
}
@Test
public void testForceStatic() {
DetachVolumeOptions options = force();
assertEquals(options.buildFormParameters().get("Force"), Collections.singletonList("true"));
}
}

View File

@ -23,6 +23,7 @@
*/ */
package org.jclouds.aws.ec2.services; package org.jclouds.aws.ec2.services;
import static org.jclouds.aws.ec2.options.DetachVolumeOptions.Builder.fromInstance;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import java.io.IOException; import java.io.IOException;
@ -37,6 +38,8 @@ import org.jclouds.aws.ec2.EC2;
import org.jclouds.aws.ec2.domain.AvailabilityZone; import org.jclouds.aws.ec2.domain.AvailabilityZone;
import org.jclouds.aws.ec2.domain.Region; import org.jclouds.aws.ec2.domain.Region;
import org.jclouds.aws.ec2.filters.FormSigner; import org.jclouds.aws.ec2.filters.FormSigner;
import org.jclouds.aws.ec2.options.DetachVolumeOptions;
import org.jclouds.aws.ec2.xml.AttachmentHandler;
import org.jclouds.aws.ec2.xml.CreateVolumeResponseHandler; import org.jclouds.aws.ec2.xml.CreateVolumeResponseHandler;
import org.jclouds.aws.ec2.xml.DescribeVolumesResponseHandler; import org.jclouds.aws.ec2.xml.DescribeVolumesResponseHandler;
import org.jclouds.aws.reference.AWSConstants; import org.jclouds.aws.reference.AWSConstants;
@ -160,6 +163,66 @@ public class ElasticBlockStoreAsyncClientTest extends RestClientTest<ElasticBloc
checkFilters(httpMethod); checkFilters(httpMethod);
} }
public void testAttachVolume() throws SecurityException, NoSuchMethodException, IOException {
Method method = ElasticBlockStoreAsyncClient.class.getMethod("attachVolumeInRegion",
Region.class, String.class, String.class, String.class);
GeneratedHttpRequest<ElasticBlockStoreAsyncClient> httpMethod = processor.createRequest(
method, Region.DEFAULT, "id", "instanceId", "/device");
assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1");
assertHeadersEqual(httpMethod,
"Content-Length: 89\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n");
assertPayloadEquals(httpMethod, "Version=2009-11-30&Action=AttachVolume&InstanceId=instanceId&VolumeId=id&Device=%2Fdevice");
assertResponseParserClassEquals(method, httpMethod, ParseSax.class);
assertSaxResponseParserClassEquals(method, AttachmentHandler.class);
assertExceptionParserClassEquals(method, null);
checkFilters(httpMethod);
}
public void testDetachVolume() throws SecurityException, NoSuchMethodException, IOException {
Method method = ElasticBlockStoreAsyncClient.class.getMethod("detachVolumeInRegion",
Region.class, String.class, Array.newInstance(DetachVolumeOptions.class, 0)
.getClass());
GeneratedHttpRequest<ElasticBlockStoreAsyncClient> httpMethod = processor.createRequest(
method, Region.DEFAULT, "id");
assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1");
assertHeadersEqual(httpMethod,
"Content-Length: 50\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n");
assertPayloadEquals(httpMethod, "Version=2009-11-30&Action=DetachVolume&VolumeId=id");
assertResponseParserClassEquals(method, httpMethod, ParseSax.class);
assertSaxResponseParserClassEquals(method, AttachmentHandler.class);
assertExceptionParserClassEquals(method, null);
checkFilters(httpMethod);
}
public void testDetachVolumeOptions() throws SecurityException, NoSuchMethodException,
IOException {
Method method = ElasticBlockStoreAsyncClient.class.getMethod("detachVolumeInRegion",
Region.class, String.class, Array.newInstance(DetachVolumeOptions.class, 0)
.getClass());
GeneratedHttpRequest<ElasticBlockStoreAsyncClient> httpMethod = processor.createRequest(
method, Region.DEFAULT, "id", fromInstance("instanceId").fromDevice("/device")
.force());
assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1");
assertHeadersEqual(httpMethod,
"Content-Length: 100\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n");
assertPayloadEquals(
httpMethod,
"Version=2009-11-30&Action=DetachVolume&VolumeId=id&InstanceId=instanceId&Device=%2Fdevice&Force=true");
assertResponseParserClassEquals(method, httpMethod, ParseSax.class);
assertSaxResponseParserClassEquals(method, AttachmentHandler.class);
assertExceptionParserClassEquals(method, null);
checkFilters(httpMethod);
}
@Override @Override
protected void checkFilters(GeneratedHttpRequest<ElasticBlockStoreAsyncClient> httpMethod) { protected void checkFilters(GeneratedHttpRequest<ElasticBlockStoreAsyncClient> httpMethod) {
assertEquals(httpMethod.getFilters().size(), 1); assertEquals(httpMethod.getFilters().size(), 1);

View File

@ -123,6 +123,16 @@ public class ElasticBlockStoreClientLiveTest {
// assertEquals(volume, result); // assertEquals(volume, result);
} }
@Test
void testAttachVolumeInRegion() {
// TODO: need an instance
}
@Test
void testDetachVolumeInRegion() {
// TODO: need an instance
}
@AfterTest @AfterTest
public void shutdown() { public void shutdown() {
context.close(); context.close();

View File

@ -0,0 +1,53 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.aws.ec2.xml;
import static org.testng.Assert.assertEquals;
import java.io.InputStream;
import org.jclouds.aws.ec2.domain.Attachment;
import org.jclouds.date.DateService;
import org.jclouds.http.functions.BaseHandlerTest;
import org.testng.annotations.Test;
/**
* Tests behavior of {@code AttachmentHandler}
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "ec2.AttachmentHandlerTest")
public class AttachmentHandlerTest extends BaseHandlerTest {
public void testApplyInputStream() {
DateService dateService = injector.getInstance(DateService.class);
InputStream is = getClass().getResourceAsStream("/ec2/attach.xml");
Attachment expected = new Attachment("vol-4d826724", "i-6058a509", "/dev/sdh",
Attachment.Status.ATTACHING, dateService
.iso8601DateParse("2008-05-07T11:51:50.000Z"));
Attachment result = factory.create(injector.getInstance(AttachmentHandler.class)).parse(is);
assertEquals(result, expected);
}
}

View File

@ -27,9 +27,9 @@ import static org.testng.Assert.assertEquals;
import java.io.InputStream; import java.io.InputStream;
import org.jclouds.aws.ec2.domain.Attachment;
import org.jclouds.aws.ec2.domain.AvailabilityZone; import org.jclouds.aws.ec2.domain.AvailabilityZone;
import org.jclouds.aws.ec2.domain.Volume; import org.jclouds.aws.ec2.domain.Volume;
import org.jclouds.aws.ec2.domain.Volume.Attachment;
import org.jclouds.date.DateService; import org.jclouds.date.DateService;
import org.jclouds.http.functions.BaseHandlerTest; import org.jclouds.http.functions.BaseHandlerTest;
import org.testng.annotations.Test; import org.testng.annotations.Test;

View File

@ -28,9 +28,9 @@ import static org.testng.Assert.assertEquals;
import java.io.InputStream; import java.io.InputStream;
import java.util.Set; import java.util.Set;
import org.jclouds.aws.ec2.domain.Attachment;
import org.jclouds.aws.ec2.domain.AvailabilityZone; import org.jclouds.aws.ec2.domain.AvailabilityZone;
import org.jclouds.aws.ec2.domain.Volume; import org.jclouds.aws.ec2.domain.Volume;
import org.jclouds.aws.ec2.domain.Volume.Attachment;
import org.jclouds.date.DateService; import org.jclouds.date.DateService;
import org.jclouds.http.functions.BaseHandlerTest; import org.jclouds.http.functions.BaseHandlerTest;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -55,7 +55,7 @@ public class DescribeVolumesResponseHandlerTest extends BaseHandlerTest {
expected.add(new Volume("vol-4282672b", 800, null, AvailabilityZone.US_EAST_1A, expected.add(new Volume("vol-4282672b", 800, null, AvailabilityZone.US_EAST_1A,
Volume.Status.IN_USE, dateService.iso8601DateParse("2008-05-07T11:51:50.000Z"), Sets Volume.Status.IN_USE, dateService.iso8601DateParse("2008-05-07T11:51:50.000Z"), Sets
.<Attachment> newHashSet(new Attachment("vol-4282672b", "i-6058a509", .<Attachment> newHashSet(new Attachment("vol-4282672b", "i-6058a509",
"/dev/sdh", Volume.Attachment.Status.ATTACHED, dateService "/dev/sdh", Attachment.Status.ATTACHED, dateService
.iso8601DateParse("2008-05-07T12:51:50.000Z"))))); .iso8601DateParse("2008-05-07T12:51:50.000Z")))));
Set<Volume> result = factory.create( Set<Volume> result = factory.create(
injector.getInstance(DescribeVolumesResponseHandler.class)).parse(is); injector.getInstance(DescribeVolumesResponseHandler.class)).parse(is);

View File

@ -0,0 +1,7 @@
<AttachVolumeResponse xmlns="http://ec2.amazonaws.com/doc/2009-11-30/">
<volumeId>vol-4d826724</volumeId>
<instanceId>i-6058a509</instanceId>
<device>/dev/sdh</device>
<status>attaching</status>
<attachTime>2008-05-07T11:51:50.000Z</attachTime>
</AttachVolumeResponse>