Replaced string representation of BlockDeviceMapping with the class for Instance(Async)Client#setBlockDeviceMappingForInstanceInRegion, getBlockDeviceMappingForInstanceInRegion

Fixed the tests and some logic for BlockDeviceMapping. NOTE: Now it receives and returns instances of RunningInstance.EbsBlockDevice
This commit is contained in:
Alex Yarmula 2010-02-20 12:23:16 -08:00
parent 118129714c
commit 0097a2ded6
11 changed files with 329 additions and 113 deletions

View File

@ -0,0 +1,53 @@
package org.jclouds.aws.ec2.binders;
import org.jclouds.aws.ec2.domain.BlockDeviceMapping;
import org.jclouds.aws.ec2.domain.RunningInstance;
import org.jclouds.aws.ec2.domain.UserIdGroupPair;
import org.jclouds.http.HttpRequest;
import org.jclouds.rest.Binder;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.String.format;
/**
* @author Oleksiy Yarmula
*/
public class BindBlockDeviceMappingToIndexedFormParams implements Binder {
private final String deviceNamePattern = "BlockDeviceMapping.%d.DeviceName";
private final String volumeIdPattern = "BlockDeviceMapping.%d.Ebs.VolumeId";
private final String deleteOnTerminationPattern = "BlockDeviceMapping.%d.Ebs.DeleteOnTermination";
@SuppressWarnings("unchecked")
public void bindToRequest(HttpRequest request, Object input) {
checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest,
"this binder is only valid for GeneratedHttpRequests");
checkArgument(checkNotNull(input, "input") instanceof BlockDeviceMapping,
"this binder is only valid for BlockDeviceMapping");
BlockDeviceMapping blockDeviceMapping = (BlockDeviceMapping) input;
GeneratedHttpRequest generatedRequest = (GeneratedHttpRequest) request;
int amazonOneBasedIndex = 1; //according to docs, counters must start with 1
for(RunningInstance.EbsBlockDevice ebsBlockDevice : blockDeviceMapping.getEbsBlockDevices()) {
//not null by contract
generatedRequest.addFormParam(format(volumeIdPattern, amazonOneBasedIndex),
ebsBlockDevice.getVolumeId());
if(ebsBlockDevice.getDeviceName() != null) {
generatedRequest.addFormParam(format(deviceNamePattern, amazonOneBasedIndex),
ebsBlockDevice.getDeviceName());
}
generatedRequest.addFormParam(format(deleteOnTerminationPattern, amazonOneBasedIndex),
String.valueOf(ebsBlockDevice.isDeleteOnTermination()));
amazonOneBasedIndex++;
}
}
}

View File

@ -0,0 +1,152 @@
package org.jclouds.aws.ec2.domain;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.inject.internal.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Date;
import java.util.List;
/**
* Defines the mapping of volumes for
* {@link org.jclouds.aws.ec2.services.InstanceClient#setBlockDeviceMappingForInstanceInRegion}.
*
* @author Oleksiy Yarmula
*/
public class BlockDeviceMapping {
private final List<RunningInstance.EbsBlockDevice> ebsBlockDevices = Lists.newArrayList();
public BlockDeviceMapping() {
}
/**
* Creates block device mapping from the list of {@link RunningInstance.EbsBlockDevice devices}.
*
* This method copies the values of the list.
* @param ebsBlockDevices
* devices to be changed for the volume
*/
public BlockDeviceMapping(List<RunningInstance.EbsBlockDevice> ebsBlockDevices) {
this.ebsBlockDevices.addAll(checkNotNull(ebsBlockDevices,
/*or throw*/ "EbsBlockDevices can't be null"));
}
/**
* Adds a {@link RunningInstance.EbsBlockDevice} to the mapping.
* @param ebsBlockDevice
* ebsBlockDevice to be added
* @return the same instance for method chaining purposes
*/
public BlockDeviceMapping addEbsBlockDevice(RunningInstance.EbsBlockDevice ebsBlockDevice) {
this.ebsBlockDevices.add(checkNotNull(ebsBlockDevice,
/*or throw*/ "EbsBlockDevice can't be null"));
return this;
}
public List<RunningInstance.EbsBlockDevice> getEbsBlockDevices() {
return ImmutableList.copyOf(ebsBlockDevices);
}
public static class EbsBlockDevice2 {
private final String volumeId;
private final String deviceName;
private final Boolean deleteOnTermination;
private final Attachment.Status attachmentStatus;
private final Date attachTime;
/**
*
* @param volumeId
* required parameter (can not be null)
* @param deviceName
* name of the device (ie "/dev/sda1")
* @param deleteOnTermination
* whether the volume will be deleted on instance termination
*/
public EbsBlockDevice2(String volumeId, @Nullable String deviceName,
@Nullable Boolean deleteOnTermination) {
this(volumeId, deviceName, null, null, deleteOnTermination);
}
/**
*
* @param volumeId
* required parameter (can not be null)
* @param deviceName
* name of the device (ie "/dev/sda1")
* @param attachmentStatus
* whether the device is attached, detached
* @param attachTime
* when the device was attached
* @param deleteOnTermination
* whether the volume will be deleted on instance termination
*/
public EbsBlockDevice2(String volumeId, @Nullable String deviceName,
@Nullable Attachment.Status attachmentStatus,
@Nullable Date attachTime, @Nullable Boolean deleteOnTermination) {
this.volumeId = checkNotNull(volumeId, /*or throw*/ "VolumeId is required");
this.deviceName = deviceName;
this.deleteOnTermination = deleteOnTermination;
this.attachmentStatus = attachmentStatus;
this.attachTime = attachTime;
}
/**
* Returns the volume id
* @return volume id. This value is never null
*/
public String getVolumeId() {
return volumeId;
}
public String getDeviceName() {
return deviceName;
}
public Boolean isDeleteOnTermination() {
return deleteOnTermination;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
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;
EbsBlockDevice2 other = (EbsBlockDevice2) obj;
if (volumeId == null) {
if (other.volumeId != null)
return false;
} else if (!volumeId.equals(other.volumeId))
return false;
if (attachTime == null) {
if (other.attachTime != null)
return false;
} else if (!attachTime.equals(other.attachTime))
return false;
if (attachmentStatus == null) {
if (other.attachmentStatus != null)
return false;
} else if (!attachmentStatus.equals(other.attachmentStatus))
return false;
return deleteOnTermination == other.deleteOnTermination;
}
}
}

View File

@ -41,17 +41,30 @@ public class RunningInstance implements Comparable<RunningInstance> {
public static class EbsBlockDevice {
private final String volumeId;
private final String deviceName;
private final Attachment.Status attachmentStatus;
private final Date attachTime;
private final boolean deleteOnTermination;
public EbsBlockDevice(String volumeId, Status attachmentStatus, Date attachTime,
boolean deleteOnTermination) {
this(volumeId, null, attachmentStatus, attachTime, deleteOnTermination);
}
public EbsBlockDevice(String volumeId, String deviceName,
Status attachmentStatus, Date attachTime,
boolean deleteOnTermination) {
super();
this.volumeId = volumeId;
this.attachmentStatus = attachmentStatus;
this.attachTime = attachTime;
this.deleteOnTermination = deleteOnTermination;
this.deviceName = deviceName;
}
public EbsBlockDevice(String volumeId, String deviceName,
boolean deleteOnTermination) {
this(volumeId, deviceName, null, null, deleteOnTermination);
}
@Override
@ -98,6 +111,10 @@ public class RunningInstance implements Comparable<RunningInstance> {
return volumeId;
}
public String getDeviceName() {
return deviceName;
}
public Attachment.Status getAttachmentStatus() {
return attachmentStatus;
}

View File

@ -30,13 +30,10 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path;
import org.jclouds.aws.domain.Region;
import org.jclouds.aws.ec2.binders.BindBlockDeviceMappingToIndexedFormParams;
import org.jclouds.aws.ec2.binders.BindInstanceIdsToIndexedFormParams;
import org.jclouds.aws.ec2.binders.IfNotNullBindAvailabilityZoneToFormParam;
import org.jclouds.aws.ec2.domain.AvailabilityZone;
import org.jclouds.aws.ec2.domain.InstanceStateChange;
import org.jclouds.aws.ec2.domain.InstanceType;
import org.jclouds.aws.ec2.domain.Reservation;
import org.jclouds.aws.ec2.domain.Image.EbsBlockDevice;
import org.jclouds.aws.ec2.domain.*;
import org.jclouds.aws.ec2.domain.Volume.InstanceInitiatedShutdownBehavior;
import org.jclouds.aws.ec2.functions.ConvertUnencodedBytesToBase64EncodedString;
import org.jclouds.aws.ec2.functions.RegionToEndpoint;
@ -222,14 +219,14 @@ public interface InstanceAsyncClient {
@FormParam("InstanceId") String instanceId);
/**
* @see AMIClient#getBlockDeviceMappingForInstanceInRegion
* @see InstanceClient#getBlockDeviceMappingForInstanceInRegion
*/
@POST
@Path("/")
@FormParams(keys = { ACTION, "Attribute" }, values = { "DescribeInstanceAttribute",
"blockDeviceMapping" })
@XMLResponseParser(BlockDeviceMappingHandler.class)
ListenableFuture<? extends Map<String, EbsBlockDevice>> getBlockDeviceMappingForInstanceInRegion(
ListenableFuture<? extends Map<String, Image.EbsBlockDevice>> getBlockDeviceMappingForInstanceInRegion(
@EndpointParam(parser = RegionToEndpoint.class) Region region,
@FormParam("InstanceId") String instanceId);
@ -320,15 +317,14 @@ public interface InstanceAsyncClient {
@FormParam("Value") InstanceInitiatedShutdownBehavior instanceInitiatedShutdownBehavior);
/**
* @see AMIClient#setBlockDeviceMappingForInstanceInRegion
* @see InstanceClient#setBlockDeviceMappingForInstanceInRegion
*/
@POST
@Path("/")
@FormParams(keys = { ACTION, "Attribute" }, values = { "ModifyInstanceAttribute",
"blockDeviceMapping" })
@FormParams(keys = { ACTION }, values = { "ModifyInstanceAttribute" })
ListenableFuture<Void> setBlockDeviceMappingForInstanceInRegion(
@EndpointParam(parser = RegionToEndpoint.class) Region region,
@FormParam("InstanceId") String instanceId,
@FormParam("Value") String blockDeviceMapping);
@BinderParam(BindBlockDeviceMappingToIndexedFormParams.class) BlockDeviceMapping blockDeviceMapping);
}

View File

@ -25,12 +25,8 @@ import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import org.jclouds.aws.domain.Region;
import org.jclouds.aws.ec2.domain.AvailabilityZone;
import org.jclouds.aws.ec2.domain.InstanceState;
import org.jclouds.aws.ec2.domain.InstanceStateChange;
import org.jclouds.aws.ec2.domain.InstanceType;
import org.jclouds.aws.ec2.domain.Reservation;
import org.jclouds.aws.ec2.domain.Image.EbsBlockDevice;
import org.jclouds.aws.ec2.domain.*;
import org.jclouds.aws.ec2.domain.RunningInstance.EbsBlockDevice;
import org.jclouds.aws.ec2.domain.Volume.InstanceInitiatedShutdownBehavior;
import org.jclouds.aws.ec2.options.RunInstancesOptions;
import org.jclouds.concurrent.Timeout;
@ -499,7 +495,7 @@ public interface InstanceClient {
InstanceInitiatedShutdownBehavior instanceInitiatedShutdownBehavior);
/**
* Sets the blockDeviceMapping used for starting the instance.
* Sets the blockDeviceMapping used for an instance.
* <p/>
* The instance needs to be in a {@link InstanceState#STOPPED} state, which implies two things:
* <ol>
@ -508,20 +504,16 @@ public interface InstanceClient {
* {@link InstanceState#STOPPING} to {@link InstanceState#STOPPED}</li>
* </ol>
*
* The {@code blockDeviceMapping} option takes a value in the following format:
* <device-name>=<snapshot-id>[:<volume-size>[:<deleteOnTermination>]]
* To create the instances of {@link RunningInstance.EbsBlockDevice},
* the constructor can be used with the following parameters:
* {@link RunningInstance.EbsBlockDevice#EbsBlockDevice(String, String, boolean)},
* that are:
* <ol>
* <li>device-name - this is the device name as it should be exposed to the instance, for example
* /dev/sdb. This is the same as the device field specified in the AttachVolume call today. This
* field also serves as the key for the structure.</li>
* <li>snapshot-id - the ID of the EBS snapshot to be used when creating the volume. This field
* is optional. If it is not specified, the volume-size field must be present to create a blank
* volume of the specified size.</li>
* <li>volume-size - the size (GiBs) of the volume. This field is optional unless no snapshot-id
* is present. If a snapshot-id is present, the size must be equal to or larger than the
* snapshot's volume size.</li>
* <li>delete-on-termination - this indicates whether the volume should be deleted on
* termination. It defaults to ' true '.</li>
* <li>Volume id (required), for instance, "vol-blah"</li>
* <li>Device name (optional), for instance, "/dev/sda1". To find out more about
* device names, read the next paragraph.</li>
* <li>Delete on termination flag (optional), which defines whether the volume will be
* deleted upon instance's termination.</li>
* </ol>
* <p/>
* Note that the device names between Linux and Windows differ. For Linux, ensure that your
@ -532,31 +524,10 @@ public interface InstanceClient {
* that they are in the form /xvd[c-p] . For example, /xvde , /xvdf and /xvdp are all valid
* Windows device names.
* <p/>
* Here are a few extra examples on how this functionality can be used.
*
* <ol>
* <li>
* resize the root volume: @{code /dev/sda1=:100}</li>
* <li>
* don't delete the root volume: {@code /dev/sda1=:100:false}</li>
* <li>
* TODO: unverified: create and attach an additional volume at launch: {@code
* /dev/sdb=snap-e8a23d81,/dev/sdc=:200}
* <p/>
* The above example will create and attach the following EBS volumes at launch time:
* <ol>
* <li>
* /dev/sdb - a 10 GiBs EBS volume containing an ext3 file system; this is an Amazon shared
* snapshot.</li>
* <li>
* /dev/sdc - an empty 200 GiB EBS volume.</li>
* </ol>
* </li>
* <li>
* TODO: unverified: cresize the root partition of a Windows 2008 image and add an additional 100
* GiB device: {@code /dev/sda1=:100,/dev/xvdc=:100}</li>
* </ol>
*
* <b>NOTE</b>: As of now 02/20/2010, this command only works to change the
* DeleteOnTermination property of the device. The volume must be <i>attached</i> to a
* stopped instance.
*
* @param region
* Instances are tied to Availability Zones. However, the instance ID is tied to the
* Region.
@ -568,5 +539,5 @@ public interface InstanceClient {
* />
*/
void setBlockDeviceMappingForInstanceInRegion(Region region, String instanceId,
String blockDeviceMapping);
BlockDeviceMapping blockDeviceMapping);
}

View File

@ -18,16 +18,19 @@
*/
package org.jclouds.aws.ec2.xml;
import java.util.Date;
import java.util.Map;
import org.jclouds.aws.ec2.domain.Image;
import org.jclouds.aws.ec2.domain.Image.EbsBlockDevice;
import com.google.inject.Inject;
import org.jclouds.aws.ec2.domain.Attachment;
import org.jclouds.aws.ec2.domain.RunningInstance.EbsBlockDevice;
import org.jclouds.date.DateService;
import org.jclouds.http.functions.ParseSax;
import com.google.common.collect.Maps;
/**
*
*
* @author Adrian Cole
*/
public class BlockDeviceMappingHandler extends
@ -36,9 +39,17 @@ public class BlockDeviceMappingHandler extends
private Map<String, EbsBlockDevice> ebsBlockDevices = Maps.newHashMap();
private String deviceName;
private String snapshotId;
private int volumeSize;
private String volumeId;
private boolean deleteOnTermination = true;// correct default is true.
private Attachment.Status attachmentStatus;
private Date attachTime;
protected final DateService dateService;
@Inject
public BlockDeviceMappingHandler(DateService dateService) {
this.dateService = dateService;
}
public Map<String, EbsBlockDevice> getResult() {
return ebsBlockDevices;
@ -47,18 +58,22 @@ public class BlockDeviceMappingHandler extends
public void endElement(String uri, String name, String qName) {
if (qName.equals("deviceName")) {
deviceName = currentText.toString().trim();
} else if (qName.equals("snapshotId")) {
snapshotId = currentText.toString().trim();
} else if (qName.equals("volumeSize")) {
volumeSize = Integer.parseInt(currentText.toString().trim());
} else if (qName.equals("volumeId")) {
volumeId = currentText.toString().trim();
} else if (qName.equals("deleteOnTermination")) {
deleteOnTermination = Boolean.parseBoolean(currentText.toString().trim());
} else if (qName.equals("status")) {
attachmentStatus = Attachment.Status.fromValue(currentText.toString().trim());
} else if (qName.equals("attachTime")) {
attachTime = dateService.iso8601DateParse(currentText.toString().trim());
} else if (qName.equals("item")) {
ebsBlockDevices.put(deviceName, new Image.EbsBlockDevice(snapshotId, volumeSize,
deleteOnTermination));
this.snapshotId = null;
this.volumeSize = 0;
this.deleteOnTermination = true;
ebsBlockDevices.put(deviceName, new EbsBlockDevice(volumeId, deviceName,
attachmentStatus, attachTime, deleteOnTermination));
this.volumeId = null;
this.deviceName = null;
this.deleteOnTermination = true;
this.attachmentStatus = null;
this.attachTime = null;
}
currentText = new StringBuilder();
}

View File

@ -36,13 +36,7 @@ import java.util.concurrent.TimeoutException;
import org.jclouds.aws.AWSResponseException;
import org.jclouds.aws.domain.Region;
import org.jclouds.aws.ec2.domain.InstanceState;
import org.jclouds.aws.ec2.domain.InstanceType;
import org.jclouds.aws.ec2.domain.IpProtocol;
import org.jclouds.aws.ec2.domain.KeyPair;
import org.jclouds.aws.ec2.domain.PublicIpInstanceIdPair;
import org.jclouds.aws.ec2.domain.Reservation;
import org.jclouds.aws.ec2.domain.RunningInstance;
import org.jclouds.aws.ec2.domain.*;
import org.jclouds.aws.ec2.domain.Image.EbsBlockDevice;
import org.jclouds.aws.ec2.domain.Volume.InstanceInitiatedShutdownBehavior;
import org.jclouds.aws.ec2.predicates.InstanceHasIpAddress;
@ -276,9 +270,10 @@ public class CloudApplicationArchitecturesEC2ClientLiveTest {
}
private void setBlockDeviceMappingForInstanceInRegion() {
BlockDeviceMapping blockDeviceMapping = new BlockDeviceMapping();
try {
client.getInstanceServices().setBlockDeviceMappingForInstanceInRegion(Region.DEFAULT,
instanceId, "whoopie");
instanceId, blockDeviceMapping);
assert false : "shouldn't be allowed, as instance needs to be ebs based-ami";
} catch (AWSResponseException e) {
assertEquals("InvalidParameterCombination", e.getError().getCode());

View File

@ -30,6 +30,7 @@ import java.io.ByteArrayInputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@ -37,19 +38,8 @@ import java.util.concurrent.TimeoutException;
import org.jclouds.aws.AWSResponseException;
import org.jclouds.aws.domain.Region;
import org.jclouds.aws.ec2.domain.Attachment;
import org.jclouds.aws.ec2.domain.Image;
import org.jclouds.aws.ec2.domain.InstanceState;
import org.jclouds.aws.ec2.domain.InstanceType;
import org.jclouds.aws.ec2.domain.IpProtocol;
import org.jclouds.aws.ec2.domain.KeyPair;
import org.jclouds.aws.ec2.domain.Reservation;
import org.jclouds.aws.ec2.domain.RootDeviceType;
import org.jclouds.aws.ec2.domain.RunningInstance;
import org.jclouds.aws.ec2.domain.Snapshot;
import org.jclouds.aws.ec2.domain.Volume;
import org.jclouds.aws.ec2.domain.*;
import org.jclouds.aws.ec2.domain.Image.Architecture;
import org.jclouds.aws.ec2.domain.Image.EbsBlockDevice;
import org.jclouds.aws.ec2.domain.Image.ImageType;
import org.jclouds.aws.ec2.domain.Volume.InstanceInitiatedShutdownBehavior;
import org.jclouds.aws.ec2.predicates.InstanceStateRunning;
@ -457,15 +447,24 @@ public class EBSBootEC2ClientLiveTest {
.getInstanceTypeForInstanceInRegion(Region.DEFAULT, ebsInstance.getId()));
}
private void setBlockDeviceMappingForInstanceInRegion() { // TODO: determine the correct
// blockDeviceMapping format
private void setBlockDeviceMappingForInstanceInRegion() {
BlockDeviceMapping blockDeviceMapping = new BlockDeviceMapping();
blockDeviceMapping.addEbsBlockDevice
(new RunningInstance.EbsBlockDevice(volume.getId(), "/dev/sda1", false));
try {
client.getInstanceServices().setBlockDeviceMappingForInstanceInRegion(Region.DEFAULT,
ebsInstance.getId(), "whoopie");
ebsInstance.getId(), blockDeviceMapping);
assertEquals(ImmutableMap.<String, EbsBlockDevice> of("whoopie", null), client
Map<String, RunningInstance.EbsBlockDevice> devices = client
.getInstanceServices().getBlockDeviceMappingForInstanceInRegion(Region.DEFAULT,
ebsInstance.getId()));
ebsInstance.getId());
assertEquals(devices.size(), 1);
RunningInstance.EbsBlockDevice device = Iterables.getOnlyElement(devices.values());
assertEquals(device.getVolumeId(), volume.getId());
assertEquals(device.getDeviceName(), "/dev/sda1");
assertEquals(device.isDeleteOnTermination(), false);
System.out.println("OK: setBlockDeviceMappingForInstanceInRegion");
} catch (Exception e) {
System.err.println("setBlockDeviceMappingForInstanceInRegion");

View File

@ -31,7 +31,9 @@ import javax.inject.Singleton;
import org.jclouds.aws.domain.Region;
import org.jclouds.aws.ec2.EC2;
import org.jclouds.aws.ec2.domain.AvailabilityZone;
import org.jclouds.aws.ec2.domain.BlockDeviceMapping;
import org.jclouds.aws.ec2.domain.InstanceType;
import org.jclouds.aws.ec2.domain.RunningInstance;
import org.jclouds.aws.ec2.domain.Volume.InstanceInitiatedShutdownBehavior;
import org.jclouds.aws.ec2.options.RunInstancesOptions;
import org.jclouds.aws.ec2.xml.BlockDeviceMappingHandler;
@ -523,20 +525,24 @@ public class InstanceAsyncClientTest extends RestClientTest<InstanceAsyncClient>
NoSuchMethodException, IOException {
Method method = InstanceAsyncClient.class
.getMethod("setBlockDeviceMappingForInstanceInRegion", Region.class, String.class,
String.class);
BlockDeviceMapping.class);
BlockDeviceMapping blockDeviceMapping = new BlockDeviceMapping();
blockDeviceMapping.addEbsBlockDevice
(new RunningInstance.EbsBlockDevice("/dev/sda1", "vol-test1", true));
GeneratedHttpRequest<InstanceAsyncClient> httpMethod = processor.createRequest(method,
Region.DEFAULT, "1", "test");
Region.DEFAULT, "1", blockDeviceMapping);
assertRequestLineEquals(httpMethod, "POST https://ec2.amazonaws.com/ HTTP/1.1");
assertHeadersEqual(httpMethod,
"Content-Length: 102\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n");
"Content-Length: 62\nContent-Type: application/x-www-form-urlencoded\nHost: ec2.amazonaws.com\n");
assertPayloadEquals(
httpMethod,
"Version=2009-11-30&Action=ModifyInstanceAttribute&Attribute=blockDeviceMapping&Value=test&InstanceId=1");
"Version=2009-11-30&Action=ModifyInstanceAttribute&InstanceId=1&BlockDeviceMapping.1.Ebs.VolumeId=%2Fdev%2Fsda1&BlockDeviceMapping.1.DeviceName=vol-test1&BlockDeviceMapping.1.Ebs.DeleteOnTermination=true");
filter.filter(httpMethod);// ensure encoding worked properly
assertPayloadEquals(
httpMethod,
"Action=ModifyInstanceAttribute&Attribute=blockDeviceMapping&InstanceId=1&Signature=KNCKfLATSmpXGuIBpXOx3lBmHv9tyu17Cxrfi%2FTzQHE%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2009-11-08T15%3A54%3A08.897Z&Value=test&Version=2009-11-30&AWSAccessKeyId=user");
"Action=ModifyInstanceAttribute&BlockDeviceMapping.1.DeviceName=vol-test1&BlockDeviceMapping.1.Ebs.DeleteOnTermination=true&BlockDeviceMapping.1.Ebs.VolumeId=%2Fdev%2Fsda1&InstanceId=1&Signature=Htpx4bm6v0O%2FcYxrsGb74NbTzCJfPtKWeuB8l2TpL%2B0%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2009-11-08T15%3A54%3A08.897Z&Version=2009-11-30&AWSAccessKeyId=user");
assertResponseParserClassEquals(method, httpMethod, CloseContentAndReturn.class);
assertSaxResponseParserClassEquals(method, null);

View File

@ -21,9 +21,12 @@ package org.jclouds.aws.ec2.xml;
import static org.testng.Assert.assertEquals;
import java.io.InputStream;
import java.util.Date;
import java.util.Map;
import org.jclouds.aws.ec2.domain.Image.EbsBlockDevice;
import org.jclouds.aws.ec2.domain.Attachment;
import org.jclouds.aws.ec2.domain.RunningInstance.EbsBlockDevice;
import org.jclouds.date.DateService;
import org.jclouds.http.functions.BaseHandlerTest;
import org.testng.annotations.Test;
@ -41,9 +44,16 @@ public class BlockDeviceMappingHandlerTest extends BaseHandlerTest {
InputStream is = getClass().getResourceAsStream(
"/ec2/describe_image_attribute_blockDeviceMapping.xml");
DateService dateService = injector.getInstance(DateService.class);
Map<String, EbsBlockDevice> expected = ImmutableMap.<String, EbsBlockDevice> of("/dev/sda1",
new EbsBlockDevice("snap-d01272b9", 30, true), "xvdf", new EbsBlockDevice(
"snap-d31272ba", 250, false));
new EbsBlockDevice("vol-d74b82be", "/dev/sda1",
Attachment.Status.ATTACHED,
dateService.iso8601DateParse("2010-02-20T18:25:26.000Z"), true),
"/dev/sdf",
new EbsBlockDevice("vol-another", "/dev/sdf",
Attachment.Status.DETACHED,
dateService.iso8601DateParse("2010-02-20T19:26:26.000Z"), false)
);
Map<String, EbsBlockDevice> result = factory.create(
injector.getInstance(BlockDeviceMappingHandler.class)).parse(is);

View File

@ -1,22 +1,24 @@
<DescribeImageAttributeResponse
xmlns="http://ec2.amazonaws.com/doc/2009-11-30/">
<imageId>ami-61a54008</imageId>
<DescribeInstanceAttributeResponse
xmlns="http://ec2.amazonaws.com/doc/2009-11-30/">
<instanceId>i-7f629614</instanceId>
<blockDeviceMapping>
<item>
<deviceName>/dev/sda1</deviceName>
<ebs>
<snapshotId>snap-d01272b9</snapshotId>
<volumeSize>30</volumeSize>
<volumeId>vol-d74b82be</volumeId>
<status>attached</status>
<attachTime>2010-02-20T18:25:26.000Z</attachTime>
<deleteOnTermination>true</deleteOnTermination>
</ebs>
</item>
<item>
<deviceName>xvdf</deviceName>
<deviceName>/dev/sdf</deviceName>
<ebs>
<snapshotId>snap-d31272ba</snapshotId>
<volumeSize>250</volumeSize>
<volumeId>vol-another</volumeId>
<status>detached</status>
<attachTime>2010-02-20T19:26:26.000Z</attachTime>
<deleteOnTermination>false</deleteOnTermination>
</ebs>
</item>
</blockDeviceMapping>
</DescribeImageAttributeResponse>
</DescribeInstanceAttributeResponse>