From 2ba0092817dc4132cf9a9469f86e24861ead00d2 Mon Sep 17 00:00:00 2001 From: Andrew Bayer Date: Thu, 19 Jun 2014 11:24:29 -0700 Subject: [PATCH] JCLOUDS-602 - Add support for volumeType, iops and encrypted to EBS. Adds CreateVolumeOptions for volume creation, adds support for the above EBS configs in Image, Volume, BlockDeviceMapping, etc. Conflicts: apis/ec2/src/main/java/org/jclouds/ec2/xml/CreateVolumeResponseHandler.java --- .../compute/options/EC2TemplateOptions.java | 19 ++- .../ec2/domain/BlockDeviceMapping.java | 82 +++++++-- .../java/org/jclouds/ec2/domain/Image.java | 39 ++++- .../java/org/jclouds/ec2/domain/Volume.java | 72 +++++++- .../ec2/features/ElasticBlockStoreApi.java | 31 ++++ .../ec2/options/CreateVolumeOptions.java | 146 ++++++++++++++++ .../RegisterImageBackedByEbsOptions.java | 115 ++++++++++++- .../ec2/options/RunInstancesOptions.java | 9 +- .../ec2/xml/CreateVolumeResponseHandler.java | 28 +++- .../xml/DescribeImagesResponseHandler.java | 48 +++--- .../jclouds/ec2/EBSBootEC2ApiLiveTest.java | 2 +- .../jclouds/ec2/features/AMIApiLiveTest.java | 6 +- .../EC2ElasticBlockStoreApiExpectTest.java | 73 +++++--- .../ElasticBlockStoreApiLiveTest.java | 37 ++++- .../ec2/options/CreateVolumeOptionsTest.java | 156 ++++++++++++++++++ .../RegisterImageBackedByEbsOptionsTest.java | 75 ++++++++- .../ec2/options/RunInstancesOptionsTest.java | 12 +- .../xml/CreateVolumeResponseHandlerTest.java | 6 +- .../DescribeImagesResponseHandlerTest.java | 16 +- .../DescribeVolumesResponseHandlerTest.java | 13 +- .../ec2/src/test/resources/created_volume.xml | 3 + .../test/resources/describe_images_ebs.xml | 4 + .../src/test/resources/describe_volumes.xml | 6 + .../resources/describe_volumes_single.xml | 3 + .../aws/ec2/domain/LaunchSpecification.java | 18 +- .../aws/ec2/features/AWSAMIApiTest.java | 33 ++-- .../options/AWSRunInstancesOptionsTest.java | 10 +- 27 files changed, 942 insertions(+), 120 deletions(-) create mode 100644 apis/ec2/src/main/java/org/jclouds/ec2/options/CreateVolumeOptions.java create mode 100644 apis/ec2/src/test/java/org/jclouds/ec2/options/CreateVolumeOptionsTest.java diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/options/EC2TemplateOptions.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/options/EC2TemplateOptions.java index 32c81244f5..0799e38150 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/options/EC2TemplateOptions.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/options/EC2TemplateOptions.java @@ -189,12 +189,27 @@ public class EC2TemplateOptions extends TemplateOptions implements Cloneable { public EC2TemplateOptions mapEBSSnapshotToDeviceName(String deviceName, String snapshotId, @Nullable Integer sizeInGib, boolean deleteOnTermination) { - blockDeviceMappings.add(new MapEBSSnapshotToDevice(deviceName, snapshotId, sizeInGib, deleteOnTermination)); + return mapEBSSnapshotToDeviceName(deviceName, snapshotId, sizeInGib, deleteOnTermination, null, null, false); + } + + public EC2TemplateOptions mapEBSSnapshotToDeviceName(String deviceName, String snapshotId, + @Nullable Integer sizeInGib, boolean deleteOnTermination, + @Nullable String volumeType, @Nullable Integer iops, + boolean encrypted) { + blockDeviceMappings.add(new MapEBSSnapshotToDevice(deviceName, snapshotId, sizeInGib, deleteOnTermination, + volumeType, iops, encrypted)); return this; } public EC2TemplateOptions mapNewVolumeToDeviceName(String deviceName, int sizeInGib, boolean deleteOnTermination) { - blockDeviceMappings.add(new MapNewVolumeToDevice(deviceName, sizeInGib, deleteOnTermination)); + return mapNewVolumeToDeviceName(deviceName, sizeInGib, deleteOnTermination, null, null, false); + } + + public EC2TemplateOptions mapNewVolumeToDeviceName(String deviceName, int sizeInGib, boolean deleteOnTermination, + @Nullable String volumeType, @Nullable Integer iops, + boolean encrypted) { + blockDeviceMappings.add(new MapNewVolumeToDevice(deviceName, sizeInGib, deleteOnTermination, volumeType, + iops, encrypted)); return this; } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/BlockDeviceMapping.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/BlockDeviceMapping.java index 47bb456c44..9094ee1a94 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/BlockDeviceMapping.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/domain/BlockDeviceMapping.java @@ -38,6 +38,9 @@ public class BlockDeviceMapping implements Comparable { private Integer sizeInGib; private Boolean noDevice; private Boolean deleteOnTermination; + private String volumeType; + private Integer iops; + private Boolean encrypted; public Builder deviceName(String deviceName) { this.deviceName = deviceName; @@ -69,8 +72,24 @@ public class BlockDeviceMapping implements Comparable { return this; } + public Builder volumeType(String volumeType) { + this.volumeType = volumeType; + return this; + } + + public Builder iops(Integer iops) { + this.iops = iops; + return this; + } + + public Builder encrypted(Boolean encrypted) { + this.encrypted = encrypted; + return this; + } + public BlockDeviceMapping build() { - return new BlockDeviceMapping(deviceName, virtualName, snapshotId, sizeInGib, noDevice, deleteOnTermination); + return new BlockDeviceMapping(deviceName, virtualName, snapshotId, sizeInGib, noDevice, deleteOnTermination, + volumeType, iops, encrypted); } public Builder clear() { @@ -80,6 +99,9 @@ public class BlockDeviceMapping implements Comparable { this.sizeInGib = null; this.noDevice = null; this.deleteOnTermination = null; + this.volumeType = null; + this.iops = null; + this.encrypted = null; return this; } } @@ -90,13 +112,17 @@ public class BlockDeviceMapping implements Comparable { private final Integer sizeInGib; private final Boolean noDevice; private final Boolean deleteOnTermination; + private final String volumeType; + private final Integer iops; + private final Boolean encrypted; // values expressed in GB private static final Integer VOLUME_SIZE_MIN_VALUE = 1; private static final Integer VOLUME_SIZE_MAX_VALUE = 1000; BlockDeviceMapping(String deviceName, @Nullable String virtualName, @Nullable String snapshotId, - @Nullable Integer sizeInGib, @Nullable Boolean noDevice, @Nullable Boolean deleteOnTermination) { + @Nullable Integer sizeInGib, @Nullable Boolean noDevice, @Nullable Boolean deleteOnTermination, + @Nullable String volumeType, @Nullable Integer iops, @Nullable Boolean encrypted) { checkNotNull(deviceName, "deviceName cannot be null"); checkNotNull(emptyToNull(deviceName), "deviceName must be defined"); @@ -111,6 +137,9 @@ public class BlockDeviceMapping implements Comparable { this.sizeInGib = sizeInGib; this.noDevice = noDevice; this.deleteOnTermination = deleteOnTermination; + this.volumeType = volumeType; + this.iops = iops; + this.encrypted = encrypted; } public String getDeviceName() { @@ -137,6 +166,18 @@ public class BlockDeviceMapping implements Comparable { return deleteOnTermination; } + public String getEbsVolumeType() { + return volumeType; + } + + public Integer getEbsIops() { + return iops; + } + + public Boolean getEbsEncrypted() { + return encrypted; + } + @Override public int hashCode() { final int prime = 31; @@ -147,6 +188,9 @@ public class BlockDeviceMapping implements Comparable { result = prime * result + ((sizeInGib == null) ? 0 : sizeInGib.hashCode()); result = prime * result + ((snapshotId == null) ? 0 : snapshotId.hashCode()); result = prime * result + ((virtualName == null) ? 0 : virtualName.hashCode()); + result = prime * result + ((volumeType == null) ? 0 : volumeType.hashCode()); + result = prime * result + ((iops == null) ? 0 : iops.hashCode()); + result = prime * result + ((encrypted == null ) ? 0 : encrypted.hashCode()); return result; } @@ -189,41 +233,59 @@ public class BlockDeviceMapping implements Comparable { return false; } else if (!virtualName.equals(other.virtualName)) return false; + if (volumeType == null) { + if (other.volumeType != null) + return false; + } else if (!volumeType.equals(other.volumeType)) + return false; + if (iops== null) { + if (other.iops != null) + return false; + } else if (!iops.equals(other.iops)) + return false; + if (encrypted == null) { + if (other.encrypted != null) + return false; + } else if (!encrypted.equals(other.encrypted)) + return false; return true; } @Override public String toString() { return "[deviceName=" + deviceName + ", virtualName=" + virtualName + ", snapshotId=" + snapshotId - + ", sizeInGib=" + sizeInGib + ", noDevice=" + noDevice + ", deleteOnTermination=" + deleteOnTermination - + "]"; + + ", sizeInGib=" + sizeInGib + ", noDevice=" + noDevice + ", deleteOnTermination=" + deleteOnTermination + + ", volumeType=" + volumeType + ", iops=" + iops + ", encrypted=" + encrypted + + "]"; } public static class MapEBSSnapshotToDevice extends BlockDeviceMapping { public MapEBSSnapshotToDevice(String deviceName, String snapshotId, @Nullable Integer sizeInGib, - @Nullable Boolean deleteOnTermination) { - super(deviceName, null, snapshotId, sizeInGib, null, deleteOnTermination); + @Nullable Boolean deleteOnTermination, @Nullable String volumeType, + @Nullable Integer iops, @Nullable Boolean encrypted) { + super(deviceName, null, snapshotId, sizeInGib, null, deleteOnTermination, volumeType, iops, encrypted); checkNotNull(emptyToNull(snapshotId), "snapshotId must be defined"); } } public static class MapNewVolumeToDevice extends BlockDeviceMapping { - public MapNewVolumeToDevice(String deviceName, Integer sizeInGib, @Nullable Boolean deleteOnTermination) { - super(deviceName, null, null, sizeInGib, null, deleteOnTermination); + public MapNewVolumeToDevice(String deviceName, Integer sizeInGib, @Nullable Boolean deleteOnTermination, + @Nullable String volumeType, @Nullable Integer iops, @Nullable Boolean encrypted) { + super(deviceName, null, null, sizeInGib, null, deleteOnTermination, volumeType, iops, encrypted); checkNotNull(sizeInGib, "sizeInGib cannot be null"); } } public static class MapEphemeralDeviceToDevice extends BlockDeviceMapping { public MapEphemeralDeviceToDevice(String deviceName, String virtualName) { - super(deviceName, virtualName, null, null, null, null); + super(deviceName, virtualName, null, null, null, null, null, null, null); checkNotNull(emptyToNull(virtualName), "virtualName must be defined"); } } public static class UnmapDeviceNamed extends BlockDeviceMapping { public UnmapDeviceNamed(String deviceName) { - super(deviceName, null, null, null, true, null); + super(deviceName, null, null, null, true, null, null, null, null); } } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/Image.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/Image.java index 6304c43782..9f24f3cd18 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/Image.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/domain/Image.java @@ -160,11 +160,18 @@ public class Image implements Comparable { private final String snapshotId; private final long volumeSize; private final boolean deleteOnTermination; + private final String volumeType; + private final Integer iops; + private final boolean encrypted; - public EbsBlockDevice(@Nullable String snapshotId, long volumeSize, boolean deleteOnTermination) { + public EbsBlockDevice(@Nullable String snapshotId, long volumeSize, boolean deleteOnTermination, + @Nullable String volumeType, @Nullable Integer iops, boolean encrypted) { this.snapshotId = snapshotId; this.volumeSize = volumeSize; this.deleteOnTermination = deleteOnTermination; + this.volumeType = volumeType; + this.iops = iops; + this.encrypted = encrypted; } public String getSnapshotId() { @@ -179,6 +186,18 @@ public class Image implements Comparable { return deleteOnTermination; } + public String getVolumeType() { + return volumeType; + } + + public Integer getIops() { + return iops; + } + + public boolean isEncrypted() { + return encrypted; + } + @Override public int hashCode() { final int prime = 31; @@ -186,6 +205,9 @@ public class Image implements Comparable { result = prime * result + (deleteOnTermination ? 1231 : 1237); result = prime * result + ((snapshotId == null) ? 0 : snapshotId.hashCode()); result = prime * result + (int) (volumeSize ^ (volumeSize >>> 32)); + result = prime * result + (encrypted ? 1249 : 1259); + result = prime * result + ((volumeType == null) ? 0 : volumeType.hashCode()); + result = prime * result + ((iops == null) ? 0 : iops.hashCode()); return result; } @@ -200,11 +222,23 @@ public class Image implements Comparable { EbsBlockDevice other = (EbsBlockDevice) obj; if (deleteOnTermination != other.deleteOnTermination) return false; + if (encrypted != other.encrypted) + return false; if (snapshotId == null) { if (other.snapshotId != null) return false; } else if (!snapshotId.equals(other.snapshotId)) return false; + if (volumeType == null) { + if (other.volumeType != null) + return false; + } else if (!volumeType.equals(other.volumeType)) + return false; + if (iops == null) { + if (other.iops != null) + return false; + } else if (!iops.equals(other.iops)) + return false; if (volumeSize != other.volumeSize) return false; return true; @@ -213,7 +247,8 @@ public class Image implements Comparable { @Override public String toString() { return "EbsBlockDevice [deleteOnTermination=" + deleteOnTermination + ", snapshotId=" + snapshotId - + ", volumeSize=" + volumeSize + "]"; + + ", volumeSize=" + volumeSize + ", volumeType=" + volumeType + ", iops=" + iops + + ", encrypted=" + encrypted + "]"; } } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/Volume.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/Volume.java index a5ca30b301..2ab87ea169 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/Volume.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/domain/Volume.java @@ -100,6 +100,11 @@ public class Volume implements Comparable { private Status status; private Date createTime; private Set attachments = ImmutableSet.of(); + @Nullable + private String volumeType; + @Nullable + private Integer iops; + private boolean encrypted = false; public Builder region(String region) { this.region = region; @@ -145,15 +150,31 @@ public class Volume implements Comparable { this.attachments = ImmutableSet.copyOf(attachments); return this; } - + + public Builder volumeType(String volumeType) { + this.volumeType = volumeType; + return this; + } + + public Builder iops(Integer iops) { + this.iops = iops; + return this; + } + + public Builder encrypted(boolean encrypted) { + this.encrypted = encrypted; + return this; + } + public Volume build() { - return new Volume(region, id, size, snapshotId, availabilityZone, status, createTime, attachments); + return new Volume(region, id, size, snapshotId, availabilityZone, status, createTime, + volumeType, iops, encrypted, attachments); } public Builder fromVolume(Volume in) { return region(in.region).id(in.id).size(in.size).snapshotId(in.snapshotId) .availabilityZone(in.availabilityZone).status(in.status).createTime(in.createTime) - .attachments(in.attachments); + .volumeType(in.volumeType).iops(in.iops).encrypted(in.encrypted).attachments(in.attachments); } } @@ -167,8 +188,14 @@ public class Volume implements Comparable { private final Date createTime; private final Set attachments; + @Nullable + private final String volumeType; + @Nullable + private final Integer iops; + private final boolean encrypted; + public Volume(String region, String id, int size, String snapshotId, String availabilityZone, Volume.Status status, - Date createTime, Iterable attachments) { + Date createTime, String volumeType, Integer iops, boolean encrypted, Iterable attachments) { this.region = checkNotNull(region, "region"); this.id = id; this.size = size; @@ -176,6 +203,9 @@ public class Volume implements Comparable { this.availabilityZone = availabilityZone; this.status = status; this.createTime = createTime; + this.volumeType = volumeType; + this.iops = iops; + this.encrypted = encrypted; this.attachments = ImmutableSet.copyOf(attachments); } @@ -214,6 +244,18 @@ public class Volume implements Comparable { return createTime; } + public String getVolumeType() { + return volumeType; + } + + public Integer getIops() { + return iops; + } + + public boolean getEncrypted() { + return encrypted; + } + public Set getAttachments() { return attachments; } @@ -230,7 +272,10 @@ public class Volume implements Comparable { result = prime * result + size; result = prime * result + ((snapshotId == null) ? 0 : snapshotId.hashCode()); result = prime * result + ((status == null) ? 0 : status.hashCode()); - return result; + result = prime * result + ((volumeType == null) ? 0 : volumeType.hashCode()); + result = prime * result + ((iops == null) ? 0 : iops.hashCode()); + result = prime * result + (encrypted ? 1249 : 1259); + return result; } @Override @@ -279,6 +324,18 @@ public class Volume implements Comparable { return false; } else if (!status.equals(other.status)) return false; + if (volumeType == null) { + if (other.volumeType != null) + return false; + } else if (!volumeType.equals(other.volumeType)) + return false; + if (iops == null) { + if (other.iops != null) + return false; + } else if (!iops.equals(other.iops)) + return false; + if (encrypted != other.encrypted) + return false; return true; } @@ -290,7 +347,8 @@ public class Volume implements Comparable { @Override public String toString() { return "Volume [attachments=" + attachments + ", availabilityZone=" + availabilityZone + ", createTime=" - + createTime + ", id=" + id + ", region=" + region + ", size=" + size + ", snapshotId=" + snapshotId - + ", status=" + status + "]"; + + createTime + ", id=" + id + ", region=" + region + ", size=" + size + ", snapshotId=" + snapshotId + + ", status=" + status + ", volumeType=" + volumeType + ", iops=" + iops + ", encrypted=" + encrypted + +"]"; } } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/features/ElasticBlockStoreApi.java b/apis/ec2/src/main/java/org/jclouds/ec2/features/ElasticBlockStoreApi.java index 69c92bf07c..83f47870a0 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/features/ElasticBlockStoreApi.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/features/ElasticBlockStoreApi.java @@ -37,6 +37,7 @@ import org.jclouds.ec2.domain.Permission; import org.jclouds.ec2.domain.Snapshot; import org.jclouds.ec2.domain.Volume; import org.jclouds.ec2.options.CreateSnapshotOptions; +import org.jclouds.ec2.options.CreateVolumeOptions; import org.jclouds.ec2.options.DescribeSnapshotsOptions; import org.jclouds.ec2.options.DetachVolumeOptions; import org.jclouds.ec2.xml.AttachmentHandler; @@ -166,6 +167,36 @@ public interface ElasticBlockStoreApi { @EndpointParam(parser = ZoneToEndpoint.class) @FormParam("AvailabilityZone") String availabilityZone, @FormParam("Size") int size); + /** + * Creates a new Amazon EBS volume to which any Amazon EC2 instance can attach within the same + * Availability Zone. For more information about Amazon EBS, go to the Amazon Elastic Compute + * Cloud Developer Guide or Amazon Elastic Compute Cloud User Guide. + * + * @param availabilityZone + * An Amazon EBS volume must be located within the same Availability Zone as the + * instance to which it attaches. + * @param options + * options like specifying a snapshot, EBS volume type, etc + * + * + * @see #describeVolumesInRegion + * @see #deleteVolumeInRegion + * @see #attachVolumeInRegion + * @see #detachVolumeInRegion + * @see AvailabilityZoneAndRegionApi#describeAvailabilityZonesInRegion + * @see + */ + @Named("CreateVolume") + @POST + @Path("/") + @FormParams(keys = ACTION, values = "CreateVolume") + @XMLResponseParser(CreateVolumeResponseHandler.class) + Volume createVolumeInAvailabilityZone( + @EndpointParam(parser = ZoneToEndpoint.class) @FormParam("AvailabilityZone") String availabilityZone, + CreateVolumeOptions... options); + /** * Describes the specified Amazon EBS volumes that you own. If you do not specify one or more * volume IDs, Amazon EBS describes all volumes that you own. For more information about Amazon diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/options/CreateVolumeOptions.java b/apis/ec2/src/main/java/org/jclouds/ec2/options/CreateVolumeOptions.java new file mode 100644 index 0000000000..9c2e1821ad --- /dev/null +++ b/apis/ec2/src/main/java/org/jclouds/ec2/options/CreateVolumeOptions.java @@ -0,0 +1,146 @@ +/* + * 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.ec2.options; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.ec2.options.internal.BaseEC2RequestOptions; + +/** + * Contains options supported in the Form API for the CreateVolume operation.

+ * Usage

The recommended way to instantiate a CreateVolumeOptions object is to statically + * import CreateVolumeOptions.Builder.* and invoke a static creation method followed by an + * instance mutator (if needed): + *

+ * + * import static org.jclouds.ec2.options.CreateVolumeOptions.Builder.* + *

+ * EC2Api connection = // get connection + * Volume volume = connection.getElasticBlockStoreApi().get().createVolumeInAvailabilityZone(availabilityZone, fromSnapshotId("123125")); + * + * + * @see + */ +public class CreateVolumeOptions extends BaseEC2RequestOptions { + + /** + * Snapshot ID to create this volume from. + */ + public CreateVolumeOptions withSize(int size) { + formParameters.put("Size", Integer.toString(size)); + return this; + } + + public int getSize() { + return Integer.parseInt(getFirstFormOrNull("Size")); + } + + /** + * Snapshot ID to create this volume from. + */ + public CreateVolumeOptions fromSnapshotId(String snapshotId) { + formParameters.put("SnapshotId", checkNotNull(snapshotId, "snapshotId")); + return this; + } + + public String getSnapshotId() { + return getFirstFormOrNull("SnapshotId"); + } + + /** + * EBS volume type to use - if not specified, will be "standard". + */ + public CreateVolumeOptions volumeType(String volumeType) { + formParameters.put("VolumeType", checkNotNull(volumeType, "volumeType")); + return this; + } + + public String getVolumeType() { + return getFirstFormOrNull("VolumeType"); + } + + /** + * EBS provisioned IOPS + */ + public CreateVolumeOptions withIops(Integer iops) { + formParameters.put("Iops", checkNotNull(iops, "iops").toString()); + return this; + } + + public Integer getIops() { + return Integer.valueOf(getFirstFormOrNull("Iops")); + } + + /** + * Should this EBS volume be encrypted? + */ + public CreateVolumeOptions isEncrypted(boolean encrypted) { + if (encrypted) + formParameters.put("Encrypted", "true"); + return this; + } + + public boolean getEncrypted() { + return Boolean.parseBoolean(getFirstFormOrNull("Encrypted")); + } + + public static class Builder { + + /** + * @see CreateVolumeOptions#fromSnapshotId(String) + */ + public static CreateVolumeOptions fromSnapshotId(String snapshotId) { + CreateVolumeOptions options = new CreateVolumeOptions(); + return options.fromSnapshotId(snapshotId); + } + + /** + * @see CreateVolumeOptions#withSize(int) + */ + public static CreateVolumeOptions withSize(int size) { + CreateVolumeOptions options = new CreateVolumeOptions(); + return options.withSize(size); + } + + /** + * @see CreateVolumeOptions#volumeType(String) + */ + public static CreateVolumeOptions volumeType(String volumeType) { + CreateVolumeOptions options = new CreateVolumeOptions(); + return options.volumeType(volumeType); + } + + /** + * @see CreateVolumeOptions#withIops(Integer) + */ + public static CreateVolumeOptions withIops(Integer iops) { + CreateVolumeOptions options = new CreateVolumeOptions(); + return options.withIops(iops); + } + + /** + * @see CreateVolumeOptions#isEncrypted(boolean) + */ + public static CreateVolumeOptions isEncrypted(boolean encrypted) { + CreateVolumeOptions options = new CreateVolumeOptions(); + return options.isEncrypted(encrypted); + } + } + +} diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/options/RegisterImageBackedByEbsOptions.java b/apis/ec2/src/main/java/org/jclouds/ec2/options/RegisterImageBackedByEbsOptions.java index 1da21c0ede..0a88c1e73a 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/options/RegisterImageBackedByEbsOptions.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/options/RegisterImageBackedByEbsOptions.java @@ -95,9 +95,90 @@ public class RegisterImageBackedByEbsOptions extends RegisterImageOptions { /** * + * adds a block device to the image from an ebs snapshot. + * + * @param deviceName + * The device name (e.g., /dev/sdh). + * @param virtualName + * The virtual device name. (nullable) + * @param snapshotId + * The ID of the snapshot. + * @param deleteOnTermination + * Whether this volume should be automatically deleted on instance termination. + * Defaults to false. + * @param volumeType + * What EBS volume type should be used. + * @param iops + * EBS provisioned IOPS for this volume. + * @param encrypted + * Whether this volume should be encrypted. + */ + public RegisterImageBackedByEbsOptions addBlockDeviceFromSnapshot(String deviceName, + @Nullable String virtualName, String snapshotId, boolean deleteOnTermination, + @Nullable String volumeType, @Nullable Integer iops, boolean encrypted) { + + addAdvancedEbsOptions(deleteOnTermination, volumeType, iops, encrypted); + addEphemeralBlockDeviceFromSnapshot(deviceName, virtualName, snapshotId); + + return this; + } + + /** + * + * adds a new block device to the image. + * + * @param deviceName + * The device name (e.g., /dev/sdh). + * @param virtualName + * The virtual device name. (nullable) + * @param volumeSize + * The size of the volume, in GiBs.. + * @param deleteOnTermination + * Whether this volume should be automatically deleted on instance termination. + * Defaults to false. + * @param volumeType + * What EBS volume type should be used. + * @param iops + * EBS provisioned IOPS for this volume. + * @param encrypted + * Whether this volume should be encrypted. + */ + public RegisterImageBackedByEbsOptions addNewBlockDevice(String deviceName, + @Nullable String virtualName, + int volumeSize, + boolean deleteOnTermination, + @Nullable String volumeType, + @Nullable Integer iops, + boolean encrypted) { + addAdvancedEbsOptions(deleteOnTermination, volumeType, iops, encrypted); + addNewEphemeralBlockDevice(deviceName, virtualName, volumeSize); + + return this; + } + + private RegisterImageBackedByEbsOptions addAdvancedEbsOptions(boolean deleteOnTermination, + @Nullable String volumeType, + @Nullable Integer iops, + boolean encrypted) { + + formParameters.put("BlockDeviceMapping." + deviceIndex + ".Ebs.DeleteOnTermination", + Boolean.toString(deleteOnTermination)); + + if (volumeType != null) + formParameters.put("BlockDeviceMapping." + deviceIndex + ".Ebs.VolumeType", volumeType); + if (iops != null) + formParameters.put("BlockDeviceMapping." + deviceIndex + ".Ebs.Iops", iops.toString()); + if (encrypted) + formParameters.put("BlockDeviceMapping." + deviceIndex + ".Ebs.Encrypted", "true"); + + return this; + } + + /** + * * adds a block device to the image from an ebs snapshot. This device is retained on instance * termination. - * + * * @param name * The device name (e.g., /dev/sdh). * @param virtualName @@ -106,7 +187,7 @@ public class RegisterImageBackedByEbsOptions extends RegisterImageOptions { * The ID of the snapshot. */ public RegisterImageBackedByEbsOptions addBlockDeviceFromSnapshot(String deviceName, - @Nullable String virtualName, String snapshotId) { + @Nullable String virtualName, String snapshotId) { formParameters.put("BlockDeviceMapping." + deviceIndex + ".Ebs.DeleteOnTermination", "false"); addEphemeralBlockDeviceFromSnapshot(deviceName, virtualName, snapshotId); return this; @@ -172,6 +253,21 @@ public class RegisterImageBackedByEbsOptions extends RegisterImageOptions { return options.addBlockDeviceFromSnapshot(deviceName, virtualName, snapshotId); } + /** + * @see RegisterImageBackedByEbsOptions#addBlockDeviceFromSnapshot(String, String, String, boolean, String, Integer, boolean) + */ + public static RegisterImageBackedByEbsOptions addBlockDeviceFromSnapshot(String deviceName, + @Nullable String virtualName, + String snapshotId, + boolean deleteOnTermination, + @Nullable String volumeType, + @Nullable Integer iops, + boolean encrypted) { + RegisterImageBackedByEbsOptions options = new RegisterImageBackedByEbsOptions(); + return options.addBlockDeviceFromSnapshot(deviceName, virtualName, snapshotId, deleteOnTermination, + volumeType, iops, encrypted); + } + /** * @see RegisterImageBackedByEbsOptions#addEphemeralBlockDeviceFromSnapshot(String, String, * String) @@ -191,6 +287,21 @@ public class RegisterImageBackedByEbsOptions extends RegisterImageOptions { return options.addNewBlockDevice(deviceName, virtualName, volumeSize); } + /** + * @see RegisterImageBackedByEbsOptions#addNewBlockDevice(String, String, int, boolean, String, Integer, boolean) + */ + public static RegisterImageBackedByEbsOptions addNewBlockDevice(String deviceName, + @Nullable String virtualName, + int volumeSize, + boolean deleteOnTermination, + @Nullable String volumeType, + @Nullable Integer iops, + boolean encrypted) { + RegisterImageBackedByEbsOptions options = new RegisterImageBackedByEbsOptions(); + return options.addNewBlockDevice(deviceName, virtualName, volumeSize, deleteOnTermination, + volumeType, iops, encrypted); + } + /** * @see RegisterImageBackedByEbsOptions#addNewEphemeralBlockDevice(String, String, int) */ diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/options/RunInstancesOptions.java b/apis/ec2/src/main/java/org/jclouds/ec2/options/RunInstancesOptions.java index 427b222b4f..e51575ef49 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/options/RunInstancesOptions.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/options/RunInstancesOptions.java @@ -140,6 +140,13 @@ public class RunInstancesOptions extends BaseEC2RequestOptions { if (mapping.getEbsDeleteOnTermination() != null) formParameters.put(String.format("BlockDeviceMapping.%d.Ebs.DeleteOnTermination", i), String.valueOf(mapping.getEbsDeleteOnTermination())); + if (mapping.getEbsVolumeType() != null) + formParameters.put(String.format("BlockDeviceMapping.%d.Ebs.VolumeType", i), mapping.getEbsVolumeType()); + if (mapping.getEbsIops() != null) + formParameters.put(String.format("BlockDeviceMapping.%d.Ebs.Iops", i), + String.valueOf(mapping.getEbsIops())); + if (mapping.getEbsEncrypted() != null) + formParameters.put(String.format("BlockDeviceMapping.%d.Ebs.Encrypted", i), String.valueOf(mapping.getEbsEncrypted())); i++; } return this; @@ -180,7 +187,7 @@ public class RunInstancesOptions extends BaseEC2RequestOptions { } /** - * @see RunInstancesOptions#asType(InstanceType) + * @see RunInstancesOptions#asType(String) */ public static RunInstancesOptions asType(String instanceType) { RunInstancesOptions options = new RunInstancesOptions(); diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/xml/CreateVolumeResponseHandler.java b/apis/ec2/src/main/java/org/jclouds/ec2/xml/CreateVolumeResponseHandler.java index ca1c77d378..55a3b8fbf3 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/xml/CreateVolumeResponseHandler.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/xml/CreateVolumeResponseHandler.java @@ -18,13 +18,16 @@ package org.jclouds.ec2.xml; import static com.google.common.base.Preconditions.checkNotNull; +import javax.inject.Inject; import java.util.Date; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import javax.inject.Inject; - +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import org.jclouds.aws.util.AWSUtils; import org.jclouds.date.DateCodec; import org.jclouds.date.DateCodecFactory; @@ -42,10 +45,6 @@ import com.google.common.base.Suppliers; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -/** - * - * @author Adrian Cole - */ public class CreateVolumeResponseHandler extends ParseSax.HandlerForGeneratedRequestWithResult { protected final DateCodec dateCodec; protected final Supplier defaultRegion; @@ -77,6 +76,9 @@ public class CreateVolumeResponseHandler extends ParseSax.HandlerForGeneratedReq protected String device; protected Attachment.Status attachmentStatus; protected Date attachTime; + protected String volumeType; + protected Integer iops; + protected boolean encrypted; protected boolean inAttachmentSet; @@ -129,6 +131,14 @@ public class CreateVolumeResponseHandler extends ParseSax.HandlerForGeneratedReq device = currentText.toString().trim(); } else if (qName.equals("attachTime")) { attachTime = dateCodec.toDate(currentText.toString().trim()); + } else if (qName.equals("volumeType")) { + volumeType = currentText.toString().trim(); + if (volumeType.equals("")) + volumeType = null; + } else if (qName.equals("iops")) { + iops = Integer.parseInt(currentText.toString().trim()); + } else if (qName.equals("encrypted")) { + encrypted = Boolean.parseBoolean(currentText.toString().trim()); } else if (qName.equals("item")) { if (inAttachmentSet) { attachments.add(new Attachment(region, volumeId, instanceId, device, attachmentStatus, attachTime)); @@ -144,7 +154,8 @@ public class CreateVolumeResponseHandler extends ParseSax.HandlerForGeneratedReq } private Volume newVolume() { - Volume volume = new Volume(region, id, size, snapshotId, availabilityZone, volumeStatus, createTime, attachments); + Volume volume = new Volume(region, id, size, snapshotId, availabilityZone, volumeStatus, createTime, + volumeType, iops, encrypted, attachments); id = null; size = 0; snapshotId = null; @@ -152,6 +163,9 @@ public class CreateVolumeResponseHandler extends ParseSax.HandlerForGeneratedReq volumeStatus = null; createTime = null; attachments = Sets.newLinkedHashSet(); + volumeType = null; + iops = null; + encrypted = false; return volume; } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/xml/DescribeImagesResponseHandler.java b/apis/ec2/src/main/java/org/jclouds/ec2/xml/DescribeImagesResponseHandler.java index b55875a539..38c17c8ab8 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/xml/DescribeImagesResponseHandler.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/xml/DescribeImagesResponseHandler.java @@ -19,29 +19,27 @@ package org.jclouds.ec2.xml; import static org.jclouds.util.SaxUtils.currentOrNull; import static org.jclouds.util.SaxUtils.equalsOrSuffix; -import java.util.Map; -import java.util.Set; - import javax.annotation.Resource; import javax.inject.Inject; - -import org.jclouds.aws.util.AWSUtils; -import org.jclouds.ec2.domain.Hypervisor; -import org.jclouds.ec2.domain.Image; -import org.jclouds.ec2.domain.RootDeviceType; -import org.jclouds.ec2.domain.VirtualizationType; -import org.jclouds.ec2.domain.Image.Architecture; -import org.jclouds.ec2.domain.Image.EbsBlockDevice; -import org.jclouds.ec2.domain.Image.ImageState; -import org.jclouds.ec2.domain.Image.ImageType; -import org.jclouds.http.functions.ParseSax; -import org.jclouds.location.Region; -import org.jclouds.logging.Logger; -import org.xml.sax.Attributes; +import java.util.Map; +import java.util.Set; import com.google.common.base.Supplier; import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import org.jclouds.aws.util.AWSUtils; +import org.jclouds.ec2.domain.Hypervisor; +import org.jclouds.ec2.domain.Image; +import org.jclouds.ec2.domain.Image.Architecture; +import org.jclouds.ec2.domain.Image.EbsBlockDevice; +import org.jclouds.ec2.domain.Image.ImageState; +import org.jclouds.ec2.domain.Image.ImageType; +import org.jclouds.ec2.domain.RootDeviceType; +import org.jclouds.ec2.domain.VirtualizationType; +import org.jclouds.http.functions.ParseSax; +import org.jclouds.location.Region; +import org.jclouds.logging.Logger; +import org.xml.sax.Attributes; /** * Parses the following XML document: @@ -95,7 +93,9 @@ public class DescribeImagesResponseHandler extends ParseSax.HandlerForGeneratedR private int volumeSize; private boolean deleteOnTermination = true;// correct default is true. - + private boolean encrypted = false; + private String volumeType; + private Integer iops; private String rootDeviceName; public Set getResult() { @@ -160,6 +160,12 @@ public class DescribeImagesResponseHandler extends ParseSax.HandlerForGeneratedR volumeSize = Integer.parseInt(currentText.toString().trim()); } else if (qName.equals("deleteOnTermination")) { deleteOnTermination = Boolean.parseBoolean(currentText.toString().trim()); + } else if (qName.equals("encrypted")) { + encrypted = Boolean.parseBoolean(currentText.toString().trim()); + } else if (qName.equals("iops")) { + iops = Integer.valueOf(currentText.toString().trim()); + } else if (qName.equals("volumeType")) { + volumeType = currentText.toString().trim(); } else if (qName.equals("ramdiskId")) { ramdiskId = currentText.toString().trim(); } else if (qName.equals("rootDeviceType")) { @@ -172,11 +178,15 @@ public class DescribeImagesResponseHandler extends ParseSax.HandlerForGeneratedR hypervisor = Hypervisor.fromValue(currentText.toString().trim()); } else if (qName.equals("item")) { if (inBlockDeviceMapping) { - ebsBlockDevices.put(deviceName, new Image.EbsBlockDevice(snapshotId, volumeSize, deleteOnTermination)); + ebsBlockDevices.put(deviceName, new Image.EbsBlockDevice(snapshotId, volumeSize, deleteOnTermination, + volumeType, iops, encrypted)); this.deviceName = null; this.snapshotId = null; this.volumeSize = 0; this.deleteOnTermination = true; + this.encrypted = false; + this.volumeType = null; + this.iops = null; } else if (!inTagSet && !inProductCodes) { try { String region = getRequest() != null ? AWSUtils.findRegionInArgsOrNull(getRequest()) : null; diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ApiLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ApiLiveTest.java index 3cff3f282a..a742a21815 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ApiLiveTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ApiLiveTest.java @@ -398,7 +398,7 @@ public class EBSBootEC2ApiLiveTest extends BaseComputeServiceContextLiveTest { assertEquals(ebsImage.getRootDeviceType(), RootDeviceType.EBS); assertEquals(ebsImage.getRootDeviceName(), "/dev/sda1"); assertEquals(ebsImage.getEbsBlockDevices().entrySet(), - ImmutableMap.of("/dev/sda1", new Image.EbsBlockDevice(snapshot.getId(), VOLUME_SIZE, true)).entrySet()); + ImmutableMap.of("/dev/sda1", new Image.EbsBlockDevice(snapshot.getId(), VOLUME_SIZE, true, "standard", null, false)).entrySet()); } private void tryToChangeStuff() { diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/features/AMIApiLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/features/AMIApiLiveTest.java index 2b56e0a380..03baba651f 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/features/AMIApiLiveTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/features/AMIApiLiveTest.java @@ -184,7 +184,7 @@ public class AMIApiLiveTest extends BaseComputeServiceContextLiveTest { // Register a new image... ebsBackedImageId = client.registerUnixImageBackedByEbsInRegion(regionId, ebsBackedImageName, snapshot.getId(), - addNewBlockDevice("/dev/sda2", "myvirtual", 1).withDescription("adrian")); + addNewBlockDevice("/dev/sda2", "myvirtual", 1, false, "gp2", null, false).withDescription("adrian")); imagesToDeregister.add(ebsBackedImageId); final Image ebsBackedImage = getOnlyElement(client.describeImagesInRegion(regionId, imageIds(ebsBackedImageId))); assertEquals(ebsBackedImage.getName(), ebsBackedImageName); @@ -194,8 +194,8 @@ public class AMIApiLiveTest extends BaseComputeServiceContextLiveTest { assertEquals(ebsBackedImage.getDescription(), "adrian"); assertEquals( ebsBackedImage.getEbsBlockDevices().entrySet(), - ImmutableMap.of("/dev/sda1", new Image.EbsBlockDevice(snapshot.getId(), snapshot.getVolumeSize(), true), - "/dev/sda2", new Image.EbsBlockDevice(null, 1, false)).entrySet()); + ImmutableMap.of("/dev/sda1", new Image.EbsBlockDevice(snapshot.getId(), snapshot.getVolumeSize(), true, "standard", null, false), + "/dev/sda2", new Image.EbsBlockDevice(null, 1, false, "gp2", null, false)).entrySet()); // List of images after - should be one larger than before int after = client.describeImagesInRegionWithFilter(regionId, diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/features/EC2ElasticBlockStoreApiExpectTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/features/EC2ElasticBlockStoreApiExpectTest.java index 9fc2338b0c..5090424684 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/features/EC2ElasticBlockStoreApiExpectTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/features/EC2ElasticBlockStoreApiExpectTest.java @@ -23,6 +23,7 @@ import org.jclouds.ec2.EC2Api; import org.jclouds.ec2.domain.Snapshot; import org.jclouds.ec2.domain.Volume; import org.jclouds.ec2.internal.BaseEC2ApiExpectTest; +import org.jclouds.ec2.options.CreateVolumeOptions; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.jclouds.rest.ResourceNotFoundException; @@ -39,14 +40,16 @@ import com.google.common.collect.ImmutableSet; @Test(groups = "unit", testName = "EC2ElasticBlockStoreApiExpectTest") public class EC2ElasticBlockStoreApiExpectTest extends BaseEC2ApiExpectTest { Volume creating = Volume.builder() - .id("vol-2a21e543") - .status(Volume.Status.CREATING) - .availabilityZone("us-east-1a") - .region("us-east-1") - .id("vol-2a21e543") - .size(1) - .createTime(dateService.iso8601DateParse("2009-12-28T05:42:53.000Z")) - .build(); + .id("vol-2a21e543") + .status(Volume.Status.CREATING) + .availabilityZone("us-east-1a") + .region("us-east-1") + .id("vol-2a21e543") + .volumeType("standard") + .iops(0) + .size(1) + .createTime(dateService.iso8601DateParse("2009-12-28T05:42:53.000Z")) + .build(); public void testCreateVolumeInAvailabilityZone() { Builder builder = ImmutableMap.builder(); @@ -66,7 +69,38 @@ public class EC2ElasticBlockStoreApiExpectTest extends BaseEC2ApiExpectTest builder = ImmutableMap.builder(); + builder.put(describeRegionsRequest, describeRegionsResponse); + builder.putAll(describeAvailabilityZonesRequestResponse); + builder.put( + HttpRequest.builder() + .method("POST") + .endpoint("https://ec2.us-east-1.amazonaws.com/") + .addHeader("Host", "ec2.us-east-1.amazonaws.com") + .payload(payloadFromStringWithContentType("Action=CreateVolume" + + "&AvailabilityZone=us-east-1a" + + "&Iops=0" + + "&Signature=uI5tXrwV4zXB3uh0OP4RkfU2HMdQ2yICfpo4gKrajMI%3D" + + "&SignatureMethod=HmacSHA256" + + "&SignatureVersion=2" + + "&Size=4" + + "&Timestamp=2012-04-16T15%3A54%3A08.897Z" + + "&Version=2010-08-31" + + "&VolumeType=standard" + + "&AWSAccessKeyId=identity", "application/x-www-form-urlencoded")).build(), + HttpResponse.builder() + .statusCode(200) + .payload(payloadFromResource("/created_volume.xml")).build()); + + ElasticBlockStoreApi client = requestsSendResponses(builder.build()).getElasticBlockStoreApi().get(); + + assertEquals(client.createVolumeInAvailabilityZone("us-east-1a", + CreateVolumeOptions.Builder.withSize(4).isEncrypted(false).volumeType("standard").withIops(0)), + creating); + } + public void testCreateVolumeFromSnapshotInAvailabilityZoneEuSetsCorrectEndpoint() { String region = "eu-west-1"; @@ -75,16 +109,17 @@ public class EC2ElasticBlockStoreApiExpectTest extends BaseEC2ApiExpectTest result = Sets.newLinkedHashSet(client.describeVolumesInRegion(defaultRegion, expected.getId())); @@ -129,6 +131,7 @@ public class ElasticBlockStoreApiLiveTest extends BaseComputeServiceContextLiveT assertEquals(result.size(), 1); Volume volume = result.iterator().next(); assertEquals(volume.getId(), expected.getId()); + assertEquals(volume.getVolumeType(), expected.getVolumeType()); } @Test(dependsOnMethods = "testCreateVolumeInAvailabilityZone") @@ -161,6 +164,24 @@ public class ElasticBlockStoreApiLiveTest extends BaseComputeServiceContextLiveT client.deleteVolumeInRegion(snapshot.getRegion(), volume.getId()); } + @Test(dependsOnMethods = "testCreateSnapshotInRegion") + void testCreateVolumeFromSnapshotInAvailabilityZoneWithOptions() { + Volume volume = client.createVolumeInAvailabilityZone(defaultZone, + fromSnapshotId(snapshot.getId())); + assertNotNull(volume); + + Predicate availabile = retry(new VolumeAvailable(client), 600, 10, SECONDS); + assert availabile.apply(volume); + + Volume result = Iterables.getOnlyElement(client.describeVolumesInRegion(snapshot.getRegion(), volume.getId())); + assertEquals(volume.getId(), result.getId()); + assertEquals(volume.getSnapshotId(), snapshot.getId()); + assertEquals(volume.getAvailabilityZone(), defaultZone); + assertEquals(result.getStatus(), Volume.Status.AVAILABLE); + + client.deleteVolumeInRegion(snapshot.getRegion(), volume.getId()); + } + @Test(dependsOnMethods = "testCreateSnapshotInRegion") void testCreateVolumeFromSnapshotInAvailabilityZoneWithSize() { Volume volume = client.createVolumeFromSnapshotInAvailabilityZone(defaultZone, 2, snapshot.getId()); diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/options/CreateVolumeOptionsTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/options/CreateVolumeOptionsTest.java new file mode 100644 index 0000000000..20f74754ca --- /dev/null +++ b/apis/ec2/src/test/java/org/jclouds/ec2/options/CreateVolumeOptionsTest.java @@ -0,0 +1,156 @@ +/* + * 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.ec2.options; + +import static org.jclouds.ec2.options.CreateVolumeOptions.Builder.fromSnapshotId; +import static org.jclouds.ec2.options.CreateVolumeOptions.Builder.isEncrypted; +import static org.jclouds.ec2.options.CreateVolumeOptions.Builder.volumeType; +import static org.jclouds.ec2.options.CreateVolumeOptions.Builder.withIops; +import static org.jclouds.ec2.options.CreateVolumeOptions.Builder.withSize; +import static org.testng.Assert.assertEquals; + +import com.google.common.collect.ImmutableList; +import org.jclouds.http.options.HttpRequestOptions; +import org.testng.annotations.Test; + +/** + * Tests possible uses of CreateVolumeOptions and CreateVolumeOptions.Builder.* + */ +public class CreateVolumeOptionsTest { + + @Test + public void testAssignability() { + assert HttpRequestOptions.class.isAssignableFrom(CreateVolumeOptions.class); + assert !String.class.isAssignableFrom(CreateVolumeOptions.class); + } + + @Test + public void testVolumeType() { + CreateVolumeOptions options = new CreateVolumeOptions(); + options.volumeType("test"); + assertEquals(options.buildFormParameters().get("VolumeType"), + ImmutableList.of("test")); + } + + @Test + public void testNullVolumeType() { + CreateVolumeOptions options = new CreateVolumeOptions(); + assertEquals(options.buildFormParameters().get("VolumeType"), ImmutableList.of()); + } + + @Test + public void testVolumeTypeStatic() { + CreateVolumeOptions options = volumeType("test"); + assertEquals(options.buildFormParameters().get("VolumeType"), + ImmutableList.of("test")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testVolmeTypeNPE() { + volumeType(null); + } + + @Test + public void testFromSnapshotId() { + CreateVolumeOptions options = new CreateVolumeOptions(); + options.fromSnapshotId("test"); + assertEquals(options.buildFormParameters().get("SnapshotId"), + ImmutableList.of("test")); + } + + @Test + public void testNullFromSnapshotId() { + CreateVolumeOptions options = new CreateVolumeOptions(); + assertEquals(options.buildFormParameters().get("SnapshotId"), ImmutableList.of()); + } + + @Test + public void testWithSnapshotIdStatic() { + CreateVolumeOptions options = fromSnapshotId("test"); + assertEquals(options.buildFormParameters().get("SnapshotId"), + ImmutableList.of("test")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testFromSnapshotIdNPE() { + fromSnapshotId(null); + } + + @Test + public void testWithIops() { + CreateVolumeOptions options = new CreateVolumeOptions(); + options.withIops(5); + assertEquals(options.buildFormParameters().get("Iops"), + ImmutableList.of("5")); + } + + @Test + public void testNullWithIops() { + CreateVolumeOptions options = new CreateVolumeOptions(); + assertEquals(options.buildFormParameters().get("Iops"), ImmutableList.of()); + } + + @Test + public void testWithIopsStatic() { + CreateVolumeOptions options = withIops(5); + assertEquals(options.buildFormParameters().get("Iops"), + ImmutableList.of("5")); + } + + @Test + public void testWithSize() { + CreateVolumeOptions options = new CreateVolumeOptions(); + options.withSize(5); + assertEquals(options.buildFormParameters().get("Size"), + ImmutableList.of("5")); + } + + @Test + public void testNullWithSize() { + CreateVolumeOptions options = new CreateVolumeOptions(); + assertEquals(options.buildFormParameters().get("Size"), ImmutableList.of()); + } + + @Test + public void testWithSizeStatic() { + CreateVolumeOptions options = withSize(5); + assertEquals(options.buildFormParameters().get("Size"), + ImmutableList.of("5")); + } + + @Test + public void testIsEncrypted() { + CreateVolumeOptions options = new CreateVolumeOptions(); + options.isEncrypted(true); + assertEquals(options.buildFormParameters().get("Encrypted"), + ImmutableList.of("true")); + } + + @Test + public void testNullIsEncrypted() { + CreateVolumeOptions options = new CreateVolumeOptions(); + assertEquals(options.buildFormParameters().get("Encrypted"), ImmutableList.of()); + } + + @Test + public void testIsEncryptedStatic() { + CreateVolumeOptions options = isEncrypted(true); + assertEquals(options.buildFormParameters().get("Encrypted"), + ImmutableList.of("true")); + } + +} diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/options/RegisterImageBackedByEbsOptionsTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/options/RegisterImageBackedByEbsOptionsTest.java index 621fdf3b2f..378d92b4e7 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/options/RegisterImageBackedByEbsOptionsTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/options/RegisterImageBackedByEbsOptionsTest.java @@ -26,13 +26,12 @@ import static org.jclouds.ec2.options.RegisterImageBackedByEbsOptions.Builder.wi import static org.jclouds.ec2.options.RegisterImageBackedByEbsOptions.Builder.withRamdisk; import static org.testng.Assert.assertEquals; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMultimap; import org.jclouds.ec2.domain.Image.Architecture; import org.jclouds.http.options.HttpRequestOptions; import org.testng.annotations.Test; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMultimap; - /** * Tests possible uses of RegisterImageBackedByEbsOptions and * RegisterImageBackedByEbsOptions.Builder.* @@ -190,6 +189,41 @@ public class RegisterImageBackedByEbsOptionsTest { addBlockDeviceFromSnapshot(null, null, null); } + @Test + public void testAdvancedAddBlockDeviceFromSnapshot() { + RegisterImageBackedByEbsOptions options = new RegisterImageBackedByEbsOptions(); + options.addBlockDeviceFromSnapshot("deviceName", "virtualName", "snapshotId", true, "gp2", 0, false); + assertEquals(options.buildFormParameters().entries(), ImmutableMultimap.builder() + .put("BlockDeviceMapping.1.Ebs.DeleteOnTermination", "true") + .put("BlockDeviceMapping.1.Ebs.VolumeType", "gp2") + .put("BlockDeviceMapping.1.Ebs.Iops", "0") + .put("BlockDeviceMapping.1.DeviceName", "deviceName") + .put("BlockDeviceMapping.1.VirtualName", "virtualName") + .put("BlockDeviceMapping.1.Ebs.SnapshotId", "snapshotId") + .build() + .entries()); + } + + @Test + public void testAdvancedAddBlockDeviceFromSnapshotStatic() { + RegisterImageBackedByEbsOptions options = addBlockDeviceFromSnapshot("deviceName", "virtualName", "snapshotId", true, "gp2", 0, true); + assertEquals(options.buildFormParameters().entries(), ImmutableMultimap.builder() + .put("BlockDeviceMapping.1.Ebs.DeleteOnTermination", "true") + .put("BlockDeviceMapping.1.Ebs.VolumeType", "gp2") + .put("BlockDeviceMapping.1.Ebs.Iops", "0") + .put("BlockDeviceMapping.1.Ebs.Encrypted", "true") + .put("BlockDeviceMapping.1.DeviceName", "deviceName") + .put("BlockDeviceMapping.1.VirtualName", "virtualName") + .put("BlockDeviceMapping.1.Ebs.SnapshotId", "snapshotId") + .build() + .entries()); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testAdvancedAddBlockDeviceFromSnapshotNPE() { + addBlockDeviceFromSnapshot(null, null, null, false, null, null, false); + } + @Test public void testAddEphemeralBlockDeviceFromSnapshot() { RegisterImageBackedByEbsOptions options = new RegisterImageBackedByEbsOptions(); @@ -269,6 +303,41 @@ public class RegisterImageBackedByEbsOptionsTest { addNewBlockDevice(null, null, 1); } + @Test + public void testAdvancedAddNewBlockDevice() { + RegisterImageBackedByEbsOptions options = new RegisterImageBackedByEbsOptions(); + options.addNewBlockDevice("deviceName", "virtualName", 5, true, "gp2", 0, true); + assertEquals(options.buildFormParameters().entries(), ImmutableMultimap.builder() + .put("BlockDeviceMapping.1.Ebs.DeleteOnTermination", "true") + .put("BlockDeviceMapping.1.Ebs.VolumeType", "gp2") + .put("BlockDeviceMapping.1.Ebs.Iops", "0") + .put("BlockDeviceMapping.1.Ebs.Encrypted", "true") + .put("BlockDeviceMapping.1.DeviceName", "deviceName") + .put("BlockDeviceMapping.1.VirtualName", "virtualName") + .put("BlockDeviceMapping.1.Ebs.VolumeSize", "5") + .build() + .entries()); + } + + @Test + public void testAdvancedAddNewBlockDeviceStatic() { + RegisterImageBackedByEbsOptions options = addNewBlockDevice("deviceName", "virtualName", 5, true, "gp2", 0, false); + assertEquals(options.buildFormParameters().entries(), ImmutableMultimap.builder() + .put("BlockDeviceMapping.1.Ebs.DeleteOnTermination", "true") + .put("BlockDeviceMapping.1.Ebs.VolumeType", "gp2") + .put("BlockDeviceMapping.1.Ebs.Iops", "0") + .put("BlockDeviceMapping.1.DeviceName", "deviceName") + .put("BlockDeviceMapping.1.VirtualName", "virtualName") + .put("BlockDeviceMapping.1.Ebs.VolumeSize", "5") + .build() + .entries()); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testAdvancedAddNewBlockDeviceNPE() { + addNewBlockDevice(null, null, 5, false, null, null, false); + } + @Test(expectedExceptions = IllegalArgumentException.class) public void testAddNewBlockDeviceTooBig() { addNewBlockDevice("deviceName", "virtualName", 1025); diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/options/RunInstancesOptionsTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/options/RunInstancesOptionsTest.java index d46535d4be..1d8d739826 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/options/RunInstancesOptionsTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/options/RunInstancesOptionsTest.java @@ -209,7 +209,7 @@ public class RunInstancesOptionsTest { @Test public void testWithBlockDeviceMapping() { - BlockDeviceMapping mapping = new BlockDeviceMapping.MapNewVolumeToDevice("/dev/sda1", 120, true); + BlockDeviceMapping mapping = new BlockDeviceMapping.MapNewVolumeToDevice("/dev/sda1", 120, true, "gp2", 10, true); RunInstancesOptions options = new RunInstancesOptions().withBlockDeviceMappings(ImmutableSet . of(mapping)); assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.DeviceName"), @@ -218,6 +218,12 @@ public class RunInstancesOptionsTest { ImmutableList.of("120")); assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.Ebs.DeleteOnTermination"), ImmutableList.of("true")); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.Ebs.VolumeType"), + ImmutableList.of("gp2")); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.Ebs.Iops"), + ImmutableList.of("10")); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.Ebs.Encrypted"), + ImmutableList.of("true")); } @Test @@ -228,7 +234,7 @@ public class RunInstancesOptionsTest { @Test public void testWithBlockDeviceMappingStatic() { - BlockDeviceMapping mapping = new BlockDeviceMapping.MapNewVolumeToDevice("/dev/sda1", 120, true); + BlockDeviceMapping mapping = new BlockDeviceMapping.MapNewVolumeToDevice("/dev/sda1", 120, true, null, null, false); RunInstancesOptions options = withBlockDeviceMappings(ImmutableSet . of(mapping)); assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.DeviceName"), @@ -237,6 +243,8 @@ public class RunInstancesOptionsTest { ImmutableList.of("120")); assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.Ebs.DeleteOnTermination"), ImmutableList.of("true")); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.Ebs.VolumeType"), + ImmutableList.of()); } @Test(expectedExceptions = NullPointerException.class) diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/xml/CreateVolumeResponseHandlerTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/xml/CreateVolumeResponseHandlerTest.java index 346c3baeef..2594b5241f 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/xml/CreateVolumeResponseHandlerTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/xml/CreateVolumeResponseHandlerTest.java @@ -43,9 +43,9 @@ public class CreateVolumeResponseHandlerTest extends BaseEC2HandlerTest { InputStream is = getClass().getResourceAsStream("/created_volume.xml"); Volume expected = new Volume(Region.US_EAST_1, "vol-2a21e543", 1, null, - "us-east-1a", Volume.Status.CREATING, dateService - .iso8601DateParse("2009-12-28T05:42:53.000Z"), Sets - . newLinkedHashSet()); + "us-east-1a", Volume.Status.CREATING, dateService + .iso8601DateParse("2009-12-28T05:42:53.000Z"), "standard", 0, false, + Sets. newLinkedHashSet()); CreateVolumeResponseHandler handler = injector.getInstance(CreateVolumeResponseHandler.class); addDefaultRegionToHandler(handler); diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeImagesResponseHandlerTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeImagesResponseHandlerTest.java index f2b08946b6..2ca7b7331d 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeImagesResponseHandlerTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeImagesResponseHandlerTest.java @@ -18,6 +18,7 @@ package org.jclouds.ec2.xml; import static com.google.common.collect.Iterables.get; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; import java.io.InputStream; import java.util.Set; @@ -85,17 +86,22 @@ public class DescribeImagesResponseHandlerTest { public void testEBS() { Set contents = ImmutableSet.of(new Image("us-east-1", Architecture.I386, "websrv_2009-12-10", - "Web Server AMI", "ami-246f8d4d", "706093390852/websrv_2009-12-10", "706093390852", - ImageState.AVAILABLE, "available", ImageType.MACHINE, true, Sets. newHashSet(), null, "windows", null, - RootDeviceType.EBS, "/dev/sda1", ImmutableMap. of("/dev/sda1", - new EbsBlockDevice("snap-d01272b9", 30, true), "xvdf", new EbsBlockDevice("snap-d31272ba", 250, - false)), ImmutableMap. of(), VirtualizationType.HVM, Hypervisor.XEN)); + "Web Server AMI", "ami-246f8d4d", "706093390852/websrv_2009-12-10", "706093390852", + ImageState.AVAILABLE, "available", ImageType.MACHINE, true, Sets. newHashSet(), null, "windows", null, + RootDeviceType.EBS, "/dev/sda1", + ImmutableMap. of("/dev/sda1", + new EbsBlockDevice("snap-d01272b9", 30, true, "standard", null, false), + "xvdf", new EbsBlockDevice("snap-d31272ba", 250, false, "standard", null, false)), + ImmutableMap. of(), VirtualizationType.HVM, Hypervisor.XEN)); Set result = parseImages("/describe_images_ebs.xml"); assertEquals(result.toString(), contents.toString()); assertEquals(get(result, 0).getImageState(), ImageState.AVAILABLE); assertEquals(get(result, 0).getRawState(), "available"); + assertEquals(get(result, 0).getEbsBlockDevices().get("/dev/sda1").getVolumeType(), "standard"); + assertEquals(get(result, 0).getEbsBlockDevices().get("/dev/sda1").isEncrypted(), false); + assertNull(get(result, 0).getEbsBlockDevices().get("/dev/sda1").getIops()); } public void testTags() { diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeVolumesResponseHandlerTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeVolumesResponseHandlerTest.java index ae286c93d2..41ff60da92 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeVolumesResponseHandlerTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeVolumesResponseHandlerTest.java @@ -45,13 +45,14 @@ public class DescribeVolumesResponseHandlerTest extends BaseEC2HandlerTest { Set expected = Sets.newLinkedHashSet(); expected.add(new Volume(defaultRegion, "vol-2a21e543", 1, null, "us-east-1a", Volume.Status.AVAILABLE, dateService.iso8601DateParse("2009-12-28T05:42:53.000Z"), - Sets. newLinkedHashSet())); + "standard", 0, false, Sets. newLinkedHashSet())); expected.add(new Volume(defaultRegion, "vol-4282672b", 800, "snap-536d1b3a", - "us-east-1a", Volume.Status.IN_USE, dateService - .iso8601DateParse("2008-05-07T11:51:50.000Z"), Sets - . newHashSet(new Attachment(defaultRegion, "vol-4282672b", "i-6058a509", - "/dev/sdh", Attachment.Status.ATTACHED, dateService - .iso8601DateParse("2008-05-07T12:51:50.000Z"))))); + "us-east-1a", Volume.Status.IN_USE, dateService + .iso8601DateParse("2008-05-07T11:51:50.000Z"), + "standard", 0, false, + Sets. newHashSet(new Attachment(defaultRegion, "vol-4282672b", "i-6058a509", + "/dev/sdh", Attachment.Status.ATTACHED, dateService + .iso8601DateParse("2008-05-07T12:51:50.000Z"))))); DescribeVolumesResponseHandler handler = injector .getInstance(DescribeVolumesResponseHandler.class); diff --git a/apis/ec2/src/test/resources/created_volume.xml b/apis/ec2/src/test/resources/created_volume.xml index acca927183..4fbcabe1cc 100644 --- a/apis/ec2/src/test/resources/created_volume.xml +++ b/apis/ec2/src/test/resources/created_volume.xml @@ -6,4 +6,7 @@ us-east-1a creating 2009-12-28T05:42:53.000Z + standard + 0 + false \ No newline at end of file diff --git a/apis/ec2/src/test/resources/describe_images_ebs.xml b/apis/ec2/src/test/resources/describe_images_ebs.xml index 12830ab032..794cd76a70 100644 --- a/apis/ec2/src/test/resources/describe_images_ebs.xml +++ b/apis/ec2/src/test/resources/describe_images_ebs.xml @@ -23,6 +23,8 @@ snap-d01272b9 30 true + standard + false @@ -31,6 +33,8 @@ snap-d31272ba 250 false + standard + false diff --git a/apis/ec2/src/test/resources/describe_volumes.xml b/apis/ec2/src/test/resources/describe_volumes.xml index 45b3efa64a..8861d6a883 100644 --- a/apis/ec2/src/test/resources/describe_volumes.xml +++ b/apis/ec2/src/test/resources/describe_volumes.xml @@ -9,6 +9,9 @@ us-east-1a available 2009-12-28T05:42:53.000Z + standard + 0 + false @@ -18,6 +21,9 @@ us-east-1a in-use 2008-05-07T11:51:50.000Z + standard + 0 + false vol-4282672b diff --git a/apis/ec2/src/test/resources/describe_volumes_single.xml b/apis/ec2/src/test/resources/describe_volumes_single.xml index fcc10a5d84..5d501ea973 100644 --- a/apis/ec2/src/test/resources/describe_volumes_single.xml +++ b/apis/ec2/src/test/resources/describe_volumes_single.xml @@ -9,6 +9,9 @@ us-east-1a in-use 2008-05-07T11:51:50.000Z + standard + 0 + false diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/LaunchSpecification.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/LaunchSpecification.java index 34c46be895..97851a7300 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/LaunchSpecification.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/LaunchSpecification.java @@ -139,12 +139,26 @@ public class LaunchSpecification { public Builder mapEBSSnapshotToDevice(String deviceName, String snapshotId, @Nullable Integer sizeInGib, boolean deleteOnTermination) { - blockDeviceMappings.add(new MapEBSSnapshotToDevice(deviceName, snapshotId, sizeInGib, deleteOnTermination)); + return mapEBSSnapshotToDevice(deviceName, snapshotId, sizeInGib, deleteOnTermination, null, null, null); + } + + public Builder mapEBSSnapshotToDevice(String deviceName, String snapshotId, @Nullable Integer sizeInGib, + boolean deleteOnTermination, @Nullable String volumeType, + @Nullable Integer iops, @Nullable Boolean encrypted) { + blockDeviceMappings.add(new MapEBSSnapshotToDevice(deviceName, snapshotId, sizeInGib, deleteOnTermination, + volumeType, iops, encrypted)); return this; } public Builder mapNewVolumeToDevice(String deviceName, int sizeInGib, boolean deleteOnTermination) { - blockDeviceMappings.add(new MapNewVolumeToDevice(deviceName, sizeInGib, deleteOnTermination)); + return mapNewVolumeToDevice(deviceName, sizeInGib, deleteOnTermination, null, null, null); + } + + public Builder mapNewVolumeToDevice(String deviceName, int sizeInGib, boolean deleteOnTermination, + @Nullable String volumeType, @Nullable Integer iops, + @Nullable Boolean encrypted) { + blockDeviceMappings.add(new MapNewVolumeToDevice(deviceName, sizeInGib, deleteOnTermination, + volumeType, iops, encrypted)); return this; } diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/AWSAMIApiTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/AWSAMIApiTest.java index 8f8a856aca..d8789c0369 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/AWSAMIApiTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/features/AWSAMIApiTest.java @@ -265,28 +265,29 @@ public class AWSAMIApiTest extends BaseAWSEC2ApiTest { } HttpRequest registerImageBackedByEBSOptions = HttpRequest.builder().method("POST") - .endpoint("https://ec2.us-east-1.amazonaws.com/") - .addHeader("Host", "ec2.us-east-1.amazonaws.com") - .addFormParam("Action", "RegisterImage") - .addFormParam("BlockDeviceMapping.0.DeviceName", "/dev/sda1") - .addFormParam("BlockDeviceMapping.0.Ebs.SnapshotId", "snapshotId") - .addFormParam("BlockDeviceMapping.1.DeviceName", "/dev/device") - .addFormParam("BlockDeviceMapping.1.Ebs.DeleteOnTermination", "false") - .addFormParam("BlockDeviceMapping.1.Ebs.SnapshotId", "snapshot") - .addFormParam("BlockDeviceMapping.2.DeviceName", "/dev/newdevice") - .addFormParam("BlockDeviceMapping.2.Ebs.DeleteOnTermination", "false") - .addFormParam("BlockDeviceMapping.2.Ebs.VolumeSize", "100") - .addFormParam("BlockDeviceMapping.2.VirtualName", "newblock") - .addFormParam("Description", "description") - .addFormParam("Name", "imageName") - .addFormParam("RootDeviceName", "/dev/sda1").build(); + .endpoint("https://ec2.us-east-1.amazonaws.com/") + .addHeader("Host", "ec2.us-east-1.amazonaws.com") + .addFormParam("Action", "RegisterImage") + .addFormParam("BlockDeviceMapping.0.DeviceName", "/dev/sda1") + .addFormParam("BlockDeviceMapping.0.Ebs.SnapshotId", "snapshotId") + .addFormParam("BlockDeviceMapping.1.DeviceName", "/dev/device") + .addFormParam("BlockDeviceMapping.1.Ebs.DeleteOnTermination", "false") + .addFormParam("BlockDeviceMapping.1.Ebs.SnapshotId", "snapshot") + .addFormParam("BlockDeviceMapping.1.Ebs.VolumeType", "gp2") + .addFormParam("BlockDeviceMapping.2.DeviceName", "/dev/newdevice") + .addFormParam("BlockDeviceMapping.2.Ebs.DeleteOnTermination", "false") + .addFormParam("BlockDeviceMapping.2.Ebs.VolumeSize", "100") + .addFormParam("BlockDeviceMapping.2.VirtualName", "newblock") + .addFormParam("Description", "description") + .addFormParam("Name", "imageName") + .addFormParam("RootDeviceName", "/dev/sda1").build(); public void testRegisterImageBackedByEBSOptions() throws SecurityException, NoSuchMethodException, IOException { Invokable method = method(AWSAMIApi.class, "registerUnixImageBackedByEbsInRegion", String.class, String.class, String.class, RegisterImageBackedByEbsOptions[].class); GeneratedHttpRequest request = processor.createRequest(method, Lists. newArrayList(null, "imageName", "snapshotId", new RegisterImageBackedByEbsOptions().withDescription("description").addBlockDeviceFromSnapshot( - "/dev/device", null, "snapshot").addNewBlockDevice("/dev/newdevice", "newblock", 100))); + "/dev/device", null, "snapshot", false, "gp2", null, false).addNewBlockDevice("/dev/newdevice", "newblock", 100))); request = (GeneratedHttpRequest) request.getFilters().get(0).filter(request); diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/options/AWSRunInstancesOptionsTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/options/AWSRunInstancesOptionsTest.java index ef75922790..6f9405ab73 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/options/AWSRunInstancesOptionsTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/options/AWSRunInstancesOptionsTest.java @@ -333,7 +333,7 @@ public class AWSRunInstancesOptionsTest { @Test public void testWithBlockDeviceMapping() { - BlockDeviceMapping mapping = new BlockDeviceMapping.MapNewVolumeToDevice("/dev/sda1", 120, true); + BlockDeviceMapping mapping = new BlockDeviceMapping.MapNewVolumeToDevice("/dev/sda1", 120, true, "gp2", 10, true); AWSRunInstancesOptions options = new AWSRunInstancesOptions().withBlockDeviceMappings(ImmutableSet . of(mapping)); assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.DeviceName"), @@ -342,6 +342,12 @@ public class AWSRunInstancesOptionsTest { ImmutableList.of("120")); assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.Ebs.DeleteOnTermination"), ImmutableList.of("true")); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.Ebs.VolumeType"), + ImmutableList.of("gp2")); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.Ebs.Iops"), + ImmutableList.of("10")); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.Ebs.Encrypted"), + ImmutableList.of("true")); } @Test @@ -352,7 +358,7 @@ public class AWSRunInstancesOptionsTest { @Test public void testWithBlockDeviceMappingStatic() { - BlockDeviceMapping mapping = new BlockDeviceMapping.MapNewVolumeToDevice("/dev/sda1", 120, true); + BlockDeviceMapping mapping = new BlockDeviceMapping.MapNewVolumeToDevice("/dev/sda1", 120, true, null, null, false); AWSRunInstancesOptions options = withBlockDeviceMappings(ImmutableSet. of(mapping)); assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.DeviceName"), ImmutableList.of("/dev/sda1"));