diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindBlockDeviceMappingToIndexedFormParams.java b/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindBlockDeviceMappingToIndexedFormParams.java index 7b51ed85a1..044cf0bdbe 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindBlockDeviceMappingToIndexedFormParams.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/binders/BindBlockDeviceMappingToIndexedFormParams.java @@ -23,14 +23,17 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static java.lang.String.format; -import org.jclouds.ec2.domain.BlockDeviceMapping; -import org.jclouds.ec2.domain.RunningInstance; +import java.util.Map; +import java.util.Map.Entry; + +import org.jclouds.ec2.domain.BlockDevice; import org.jclouds.http.HttpRequest; import org.jclouds.http.utils.ModifyRequest; import org.jclouds.rest.Binder; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.ImmutableMultimap.Builder; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; /** * @author Oleksiy Yarmula @@ -44,29 +47,22 @@ public class BindBlockDeviceMappingToIndexedFormParams implements Binder { @Override public R bindToRequest(R request, Object input) { - checkArgument(checkNotNull(input, "input") instanceof BlockDeviceMapping, - "this binder is only valid for BlockDeviceMapping"); - BlockDeviceMapping blockDeviceMapping = (BlockDeviceMapping) input; + checkArgument(checkNotNull(input, "input") instanceof Map, "this binder is only valid for Map"); + @SuppressWarnings("unchecked") + Map blockDeviceMapping = (Map) input; - Builder builder = ImmutableMultimap. builder(); + com.google.common.collect.ImmutableMap.Builder builder = ImmutableMap. builder(); int amazonOneBasedIndex = 1; // according to docs, counters must start with 1 - for (String ebsBlockDeviceName : blockDeviceMapping.getEbsBlockDevices().keySet()) { - for (RunningInstance.EbsBlockDevice ebsBlockDevice : blockDeviceMapping.getEbsBlockDevices().get( - ebsBlockDeviceName)) { + for (Entry ebsBlockDeviceName : blockDeviceMapping.entrySet()) { + // not null by contract + builder.put(format(volumeIdPattern, amazonOneBasedIndex), ebsBlockDeviceName.getValue().getVolumeId()); + builder.put(format(deviceNamePattern, amazonOneBasedIndex), ebsBlockDeviceName.getKey()); + builder.put(format(deleteOnTerminationPattern, amazonOneBasedIndex), + String.valueOf(ebsBlockDeviceName.getValue().isDeleteOnTermination())); - // not null by contract - builder.put(format(volumeIdPattern, amazonOneBasedIndex), ebsBlockDevice.getVolumeId()); - - if (ebsBlockDeviceName != null) { - builder.put(format(deviceNamePattern, amazonOneBasedIndex), ebsBlockDeviceName); - } - builder.put(format(deleteOnTerminationPattern, amazonOneBasedIndex), - String.valueOf(ebsBlockDevice.isDeleteOnTermination())); - - amazonOneBasedIndex++; - } + amazonOneBasedIndex++; } - ImmutableMultimap forms = builder.build(); + Multimap forms = Multimaps.forMap(builder.build()); return forms.size() == 0 ? request : ModifyRequest.putFormParams(request, forms); } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java index 9b4547cb6e..3cac97d04e 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java @@ -32,10 +32,10 @@ import javax.inject.Inject; import javax.inject.Singleton; import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.domain.BlockDevice; import org.jclouds.ec2.domain.InstanceState; import org.jclouds.ec2.domain.RootDeviceType; import org.jclouds.ec2.domain.RunningInstance; -import org.jclouds.ec2.domain.RunningInstance.EbsBlockDevice; import org.jclouds.collect.Memoized; import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.HardwareBuilder; @@ -131,10 +131,10 @@ public class RunningInstanceToNodeMetadata implements Function addEBS(final RunningInstance instance, Iterable volumes) { Iterable ebsVolumes = Iterables.transform(instance.getEbsBlockDevices().entrySet(), - new Function, Volume>() { + new Function, Volume>() { @Override - public Volume apply(Entry from) { + public Volume apply(Entry from) { return new VolumeImpl(from.getValue().getVolumeId(), Volume.Type.SAN, null, from.getKey(), instance.getRootDeviceName() != null && instance.getRootDeviceName().equals(from.getKey()), true); diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/internal/EC2TemplateBuilderImpl.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/internal/EC2TemplateBuilderImpl.java index d7b709631f..05da0cff62 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/internal/EC2TemplateBuilderImpl.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/internal/EC2TemplateBuilderImpl.java @@ -29,8 +29,6 @@ import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; -import org.jclouds.ec2.compute.domain.RegionAndName; -import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.jclouds.collect.Memoized; import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.Image; @@ -38,6 +36,8 @@ import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.domain.internal.TemplateBuilderImpl; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.domain.Location; +import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.compute.options.EC2TemplateOptions; import com.google.common.base.Supplier; import com.google.common.collect.ComputationException; @@ -73,6 +73,7 @@ public class EC2TemplateBuilderImpl extends TemplateBuilderImpl { eTo.noKeyPair(); if (eFrom.getSubnetId() != null) eTo.subnetId(eFrom.getSubnetId()); + eTo.blockDeviceMappings(eFrom.getBlockDeviceMappings()); if (eFrom.isMonitoringEnabled()) eTo.enableMonitoring(); if (eFrom.getUserData() != null) 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 f869a757fd..be4932c2d4 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 @@ -24,10 +24,18 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import java.util.Arrays; +import java.util.HashSet; import java.util.Set; +import javax.annotation.Nullable; + import org.jclouds.compute.options.TemplateOptions; import org.jclouds.domain.Credentials; +import org.jclouds.ec2.domain.BlockDeviceMapping; +import org.jclouds.ec2.domain.BlockDeviceMapping.MapEBSSnapshotToDevice; +import org.jclouds.ec2.domain.BlockDeviceMapping.MapEphemeralDeviceToDevice; +import org.jclouds.ec2.domain.BlockDeviceMapping.MapNewVolumeToDevice; +import org.jclouds.ec2.domain.BlockDeviceMapping.UnmapDeviceNamed; import org.jclouds.io.Payload; import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.util.Preconditions2; @@ -43,7 +51,7 @@ import com.google.common.collect.Iterables; * needed): *

* - * import static org.jclouds.ec2.compute.options.EC2TemplateOptions.Builder.*; + * import static org.jclouds.aws.ec2.compute.options.EC2TemplateOptions.Builder.*; *

* ComputeService client = // get connection * templateBuilder.options(inboundPorts(22, 80, 8080, 443)); @@ -62,6 +70,7 @@ public class EC2TemplateOptions extends TemplateOptions { private boolean noPlacementGroup; private String subnetId; private byte[] userData; + private Set blockDeviceMappings = ImmutableSet.of(); public static final EC2TemplateOptions NONE = new EC2TemplateOptions(); @@ -94,17 +103,16 @@ public class EC2TemplateOptions extends TemplateOptions { return this; } - /** * Unencoded data */ public EC2TemplateOptions userData(byte[] unencodedData) { checkArgument(checkNotNull(unencodedData, "unencodedData").length <= 16 * 1024, - "userData cannot be larger than 16kb"); + "userData cannot be larger than 16kb"); this.userData = unencodedData; return this; } - + /** * Specifies the keypair used to run instances with */ @@ -155,6 +163,81 @@ public class EC2TemplateOptions extends TemplateOptions { return this; } + /** + * Specifies the block device mappings to be used to run the instance + */ + public EC2TemplateOptions mapEBSSnapshotToDeviceName(String deviceName, String snapshotId, + @Nullable Integer sizeInGib, @Nullable Boolean deleteOnTermination) { + checkNotNull(deviceName, "deviceName cannot be null"); + Preconditions2.checkNotEmpty(deviceName, "deviceName must be non-empty"); + checkNotNull(snapshotId, "snapshotId cannot be null"); + Preconditions2.checkNotEmpty(snapshotId, "snapshotId must be non-empty"); + Set mappings = new HashSet(); + mappings.addAll(blockDeviceMappings); + MapEBSSnapshotToDevice mapping = new MapEBSSnapshotToDevice(deviceName, snapshotId, sizeInGib, + deleteOnTermination); + mappings.add(mapping); + blockDeviceMappings = ImmutableSet.copyOf(mappings); + return this; + } + + /** + * Specifies the block device mappings to be used to run the instance + */ + public EC2TemplateOptions mapNewVolumeToDeviceName(String deviceName, Integer sizeInGib, + @Nullable Boolean deleteOnTermination) { + checkNotNull(deviceName, "deviceName cannot be null"); + Preconditions2.checkNotEmpty(deviceName, "deviceName must be non-empty"); + checkNotNull(sizeInGib, "sizeInGib cannot be null"); + + Set mappings = new HashSet(); + mappings.addAll(blockDeviceMappings); + MapNewVolumeToDevice mapping = new MapNewVolumeToDevice(deviceName, sizeInGib, deleteOnTermination); + mappings.add(mapping); + blockDeviceMappings = ImmutableSet.copyOf(mappings); + return this; + } + + /** + * Specifies the block device mappings to be used to run the instance + */ + public EC2TemplateOptions mapEphemeralDeviceToDeviceName(String deviceName, String virtualName) { + checkNotNull(deviceName, "deviceName cannot be null"); + Preconditions2.checkNotEmpty(deviceName, "deviceName must be non-empty"); + checkNotNull(virtualName, "virtualName cannot be null"); + Preconditions2.checkNotEmpty(virtualName, "virtualName must be non-empty"); + + Set mappings = new HashSet(); + mappings.addAll(blockDeviceMappings); + MapEphemeralDeviceToDevice mapping = new MapEphemeralDeviceToDevice(deviceName, virtualName); + mappings.add(mapping); + blockDeviceMappings = ImmutableSet.copyOf(mappings); + return this; + } + + /** + * Specifies the block device mappings to be used to run the instance + */ + public EC2TemplateOptions unmapDeviceNamed(String deviceName) { + checkNotNull(deviceName, "deviceName cannot be null"); + Preconditions2.checkNotEmpty(deviceName, "deviceName must be non-empty"); + + Set mappings = new HashSet(); + mappings.addAll(blockDeviceMappings); + UnmapDeviceNamed mapping = new UnmapDeviceNamed(deviceName); + mappings.add(mapping); + blockDeviceMappings = ImmutableSet.copyOf(mappings); + return this; + } + + /** + * Specifies the block device mappings to be used to run the instance + */ + public EC2TemplateOptions blockDeviceMappings(Set blockDeviceMappings) { + this.blockDeviceMappings = ImmutableSet.copyOf(checkNotNull(blockDeviceMappings, "blockDeviceMappings")); + return this; + } + public static class Builder { /** @@ -180,7 +263,7 @@ public class EC2TemplateOptions extends TemplateOptions { EC2TemplateOptions options = new EC2TemplateOptions(); return EC2TemplateOptions.class.cast(options.keyPair(keyPair)); } - + /** * @see EC2TemplateOptions#userData */ @@ -277,7 +360,6 @@ public class EC2TemplateOptions extends TemplateOptions { EC2TemplateOptions options = new EC2TemplateOptions(); return EC2TemplateOptions.class.cast(options.subnetId(subnetId)); } - } // methods that only facilitate returning the correct object type @@ -454,7 +536,7 @@ public class EC2TemplateOptions extends TemplateOptions { public String getSubnetId() { return subnetId; } - + /** * @return unencoded user data. */ @@ -462,18 +544,27 @@ public class EC2TemplateOptions extends TemplateOptions { return userData; } + /** + * @return BlockDeviceMapping to use when running the instance or null. + */ + public Set getBlockDeviceMappings() { + return blockDeviceMappings; + } + @Override public int hashCode() { + final int prime = 31; int result = super.hashCode(); + result = prime * result + ((blockDeviceMappings == null) ? 0 : blockDeviceMappings.hashCode()); result = prime * result + ((groupIds == null) ? 0 : groupIds.hashCode()); result = prime * result + ((keyPair == null) ? 0 : keyPair.hashCode()); + result = prime * result + (monitoringEnabled ? 1231 : 1237); result = prime * result + (noKeyPair ? 1231 : 1237); result = prime * result + (noPlacementGroup ? 1231 : 1237); - result = prime * result + (monitoringEnabled ? 1231 : 1237); result = prime * result + ((placementGroup == null) ? 0 : placementGroup.hashCode()); result = prime * result + ((subnetId == null) ? 0 : subnetId.hashCode()); - result = prime * result + ((userData == null) ? 0 : userData.hashCode()); + result = prime * result + Arrays.hashCode(userData); return result; } @@ -486,6 +577,11 @@ public class EC2TemplateOptions extends TemplateOptions { if (getClass() != obj.getClass()) return false; EC2TemplateOptions other = (EC2TemplateOptions) obj; + if (blockDeviceMappings == null) { + if (other.blockDeviceMappings != null) + return false; + } else if (!blockDeviceMappings.equals(other.blockDeviceMappings)) + return false; if (groupIds == null) { if (other.groupIds != null) return false; @@ -496,12 +592,12 @@ public class EC2TemplateOptions extends TemplateOptions { return false; } else if (!keyPair.equals(other.keyPair)) return false; + if (monitoringEnabled != other.monitoringEnabled) + return false; if (noKeyPair != other.noKeyPair) return false; if (noPlacementGroup != other.noPlacementGroup) return false; - if (monitoringEnabled != other.monitoringEnabled) - return false; if (placementGroup == null) { if (other.placementGroup != null) return false; @@ -512,21 +608,19 @@ public class EC2TemplateOptions extends TemplateOptions { return false; } else if (!subnetId.equals(other.subnetId)) return false; - if (userData == null) { - if (other.userData != null) - return false; - } else if (!userData.equals(other.userData)) + if (!Arrays.equals(userData, other.userData)) return false; + return true; } @Override public String toString() { - return "[groupIds=" + groupIds + ", keyPair=" + keyPair + ", noKeyPair=" + noKeyPair + ", placementGroup=" - + placementGroup + ", noPlacementGroup=" + noPlacementGroup + ", monitoringEnabled=" + monitoringEnabled - + ", inboundPorts=" + Arrays.toString(inboundPorts) + ", privateKey=" + (privateKey != null) - + ", publicKey=" + (publicKey != null) + ", runScript=" + (script != null) + ", port:seconds=" + port - + ":" + seconds + ", subnetId=" + subnetId + ", metadata/details: " + includeMetadata + "]"; + + return "EC2TemplateOptions [groupIds=" + groupIds + ", keyPair=" + keyPair + ", noKeyPair=" + noKeyPair + + ", monitoringEnabled=" + monitoringEnabled + ", placementGroup=" + placementGroup + ", noPlacementGroup=" + + noPlacementGroup + ", subnetId=" + subnetId + ", userData=" + Arrays.toString(userData) + + ", blockDeviceMappings=" + blockDeviceMappings + "]"; } } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java index e33fa87190..6da51f0e16 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java @@ -19,6 +19,7 @@ package org.jclouds.ec2.compute.strategy; +import static com.google.common.base.Preconditions.checkState; import static org.jclouds.ec2.options.RunInstancesOptions.Builder.asType; import java.util.Map; @@ -29,16 +30,17 @@ import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.options.TemplateOptions; import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; import org.jclouds.ec2.compute.functions.CreatePlacementGroupIfNeeded; import org.jclouds.ec2.compute.functions.CreateSecurityGroupIfNeeded; import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair; import org.jclouds.ec2.compute.options.EC2TemplateOptions; +import org.jclouds.ec2.domain.BlockDeviceMapping; import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.options.RunInstancesOptions; -import org.jclouds.compute.domain.Template; -import org.jclouds.compute.options.TemplateOptions; import org.jclouds.location.Provider; import com.google.common.annotations.VisibleForTesting; @@ -114,6 +116,13 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions if (userData != null) instanceOptions.withUserData(userData); + Set blockDeviceMappings = EC2TemplateOptions.class.cast(template.getOptions()) + .getBlockDeviceMappings(); + if (blockDeviceMappings != null && blockDeviceMappings.size() > 0) { + checkState("ebs".equals(template.getImage().getUserMetadata().get("rootDeviceType")), + "BlockDeviceMapping only available on ebs boot"); + instanceOptions.withBlockDeviceMappings(blockDeviceMappings); + } return instanceOptions; } @@ -189,4 +198,4 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions } return groups; } -} +} \ No newline at end of file diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/BlockDevice.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/BlockDevice.java new file mode 100644 index 0000000000..3a98726d48 --- /dev/null +++ b/apis/ec2/src/main/java/org/jclouds/ec2/domain/BlockDevice.java @@ -0,0 +1,107 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.domain; + +import java.util.Date; + +import org.jclouds.ec2.domain.Attachment.Status; + +/** + * + * @see + * @author Adrian Cole + */ +public class BlockDevice { + private final String volumeId; + private final Attachment.Status attachmentStatus; + private final Date attachTime; + private final boolean deleteOnTermination; + + public BlockDevice(String volumeId, Status attachmentStatus, Date attachTime, boolean deleteOnTermination) { + super(); + this.volumeId = volumeId; + this.attachmentStatus = attachmentStatus; + this.attachTime = attachTime; + this.deleteOnTermination = deleteOnTermination; + } + + public BlockDevice(String volumeId, boolean deleteOnTermination) { + this(volumeId, null, null, deleteOnTermination); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((attachTime == null) ? 0 : attachTime.hashCode()); + result = prime * result + ((attachmentStatus == null) ? 0 : attachmentStatus.hashCode()); + result = prime * result + (deleteOnTermination ? 1231 : 1237); + 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; + BlockDevice other = (BlockDevice) obj; + 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; + if (deleteOnTermination != other.deleteOnTermination) + return false; + if (volumeId == null) { + if (other.volumeId != null) + return false; + } else if (!volumeId.equals(other.volumeId)) + return false; + return true; + } + + public String getVolumeId() { + return volumeId; + } + + public Attachment.Status getAttachmentStatus() { + return attachmentStatus; + } + + public Date getAttachTime() { + return attachTime; + } + + public boolean isDeleteOnTermination() { + return deleteOnTermination; + } + +} \ No newline at end of file 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 ca25406513..94364d10e6 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 @@ -19,59 +19,130 @@ package org.jclouds.ec2.domain; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import javax.annotation.Nullable; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Multimap; +import org.jclouds.util.Preconditions2; /** - * Defines the mapping of volumes for - * {@link org.jclouds.ec2.services.InstanceClient#setBlockDeviceMappingForInstanceInRegion}. * - * @author Oleksiy Yarmula + * @author Lili Nadar */ public class BlockDeviceMapping { + private final String deviceName; + private final String virtualName; + private final String snapshotId; + private final Integer sizeInGib; + private final Boolean noDevice; + private final Boolean deleteOnTermination; - private final Multimap ebsBlockDevices = LinkedHashMultimap - .create(); + // values expressed in GB + private static final Integer VOLUME_SIZE_MIN_VALUE = 1; + private static final Integer VOLUME_SIZE_MAX_VALUE = 1000; - public BlockDeviceMapping() { + public BlockDeviceMapping(String deviceName, @Nullable String virtualName, @Nullable String snapshotId, + @Nullable Integer sizeInGib, @Nullable Boolean noDevice, @Nullable Boolean deleteOnTermination) { + + checkNotNull(deviceName, "deviceName cannot be null"); + Preconditions2.checkNotEmpty(deviceName, "the deviceName must be non-empty"); + + if (sizeInGib != null) { + checkArgument((sizeInGib >= VOLUME_SIZE_MIN_VALUE && sizeInGib <= VOLUME_SIZE_MAX_VALUE), + String.format("Size in Gib must be between %s and %s GB", VOLUME_SIZE_MIN_VALUE, VOLUME_SIZE_MAX_VALUE)); + } + this.deviceName = deviceName; + this.virtualName = virtualName; + this.snapshotId = snapshotId; + this.sizeInGib = sizeInGib; + this.noDevice = noDevice; + this.deleteOnTermination = deleteOnTermination; } - /** - * 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. This cannot be null. - */ - public BlockDeviceMapping(Multimap ebsBlockDevices) { - this.ebsBlockDevices.putAll(checkNotNull(ebsBlockDevices, - /* or throw */"EbsBlockDevices can't be null")); + public String getDeviceName() { + return deviceName; } - /** - * Adds a {@link RunningInstance.EbsBlockDevice} to the mapping. - * - * @param deviceName - * name of the device to apply the mapping. Can be null. - * @param ebsBlockDevice - * ebsBlockDevice to be added. This cannot be null. - * @return the same instance for method chaining purposes - */ - public BlockDeviceMapping addEbsBlockDevice(@Nullable String deviceName, - RunningInstance.EbsBlockDevice ebsBlockDevice) { - this.ebsBlockDevices.put(deviceName, checkNotNull(ebsBlockDevice, - /* or throw */"EbsBlockDevice can't be null")); - return this; + public String getVirtualName() { + return virtualName; } - public Multimap getEbsBlockDevices() { - return ImmutableMultimap. builder().putAll( - ebsBlockDevices).build(); + public String getEbsSnapshotId() { + return snapshotId; } -} + + public Integer getEbsVolumeSize() { + return sizeInGib; + } + + public Boolean getEbsNoDevice() { + return noDevice; + } + + public Boolean getEbsDeleteOnTermination() { + return deleteOnTermination; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((deviceName == null) ? 0 : deviceName.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; + BlockDeviceMapping other = (BlockDeviceMapping) obj; + if (deviceName == null) { + if (other.deviceName != null) + return false; + } else if (!deviceName.equals(other.deviceName)) + return false; + return true; + } + + @Override + public String toString() { + return "BlockDeviceMapping [deviceName=" + deviceName + ", virtualName=" + virtualName + ", snapshotId=" + + snapshotId + ", sizeInGib=" + sizeInGib + ", noDevice=" + noDevice + ", deleteOnTermination=" + + deleteOnTermination + "]"; + } + + 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); + checkNotNull(snapshotId, "snapshotId cannot be null"); + Preconditions2.checkNotEmpty(snapshotId, "the snapshotId must be non-empty"); + } + } + + public static class MapNewVolumeToDevice extends BlockDeviceMapping { + public MapNewVolumeToDevice(String deviceName, Integer sizeInGib, @Nullable Boolean deleteOnTermination) { + super(deviceName, null, null, sizeInGib, null, deleteOnTermination); + 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); + checkNotNull(virtualName, "virtualName cannot be null"); + Preconditions2.checkNotEmpty(virtualName, "the virtualName must be non-empty"); + } + } + + public static class UnmapDeviceNamed extends BlockDeviceMapping { + public UnmapDeviceNamed(String deviceName) { + super(deviceName, null, null, null, true, null); + } + } +} \ No newline at end of file diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/RunningInstance.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/RunningInstance.java index 7fde17c36d..645de2b7b9 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/RunningInstance.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/domain/RunningInstance.java @@ -27,7 +27,6 @@ import java.util.Set; import javax.annotation.Nullable; -import org.jclouds.ec2.domain.Attachment.Status; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; @@ -41,82 +40,6 @@ import com.google.common.collect.Sets; */ public class RunningInstance implements Comparable { - public static class EbsBlockDevice { - private final String volumeId; - private final Attachment.Status attachmentStatus; - private final Date attachTime; - private final boolean deleteOnTermination; - - public EbsBlockDevice(String volumeId, Status attachmentStatus, Date attachTime, boolean deleteOnTermination) { - super(); - this.volumeId = volumeId; - this.attachmentStatus = attachmentStatus; - this.attachTime = attachTime; - this.deleteOnTermination = deleteOnTermination; - } - - public EbsBlockDevice(String volumeId, boolean deleteOnTermination) { - this(volumeId, null, null, deleteOnTermination); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((attachTime == null) ? 0 : attachTime.hashCode()); - result = prime * result + ((attachmentStatus == null) ? 0 : attachmentStatus.hashCode()); - result = prime * result + (deleteOnTermination ? 1231 : 1237); - 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; - EbsBlockDevice other = (EbsBlockDevice) obj; - 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; - if (deleteOnTermination != other.deleteOnTermination) - return false; - if (volumeId == null) { - if (other.volumeId != null) - return false; - } else if (!volumeId.equals(other.volumeId)) - return false; - return true; - } - - public String getVolumeId() { - return volumeId; - } - - public Attachment.Status getAttachmentStatus() { - return attachmentStatus; - } - - public Date getAttachTime() { - return attachTime; - } - - public boolean isDeleteOnTermination() { - return deleteOnTermination; - } - - } - private final String region; private final Set groupIds = Sets.newLinkedHashSet(); @@ -159,7 +82,7 @@ public class RunningInstance implements Comparable { private final RootDeviceType rootDeviceType; @Nullable private final String rootDeviceName; - private final Map ebsBlockDevices = Maps.newHashMap(); + private final Map ebsBlockDevices = Maps.newHashMap(); public int compareTo(RunningInstance o) { return (this == o) ? 0 : getId().compareTo(o.getId()); @@ -173,7 +96,7 @@ public class RunningInstance implements Comparable { @Nullable String privateIpAddress, Set productCodes, @Nullable String ramdiskId, @Nullable String reason, @Nullable String subnetId, @Nullable String spotInstanceRequestId, @Nullable String vpcId, RootDeviceType rootDeviceType, @Nullable String rootDeviceName, - Map ebsBlockDevices) { + Map ebsBlockDevices) { Iterables.addAll(this.groupIds, checkNotNull(groupIds, "groupIds")); this.region = checkNotNull(region, "region"); this.amiLaunchIndex = amiLaunchIndex; // nullable on runinstances. @@ -389,7 +312,7 @@ public class RunningInstance implements Comparable { /** * EBS volumes associated with the instance. */ - public Map getEbsBlockDevices() { + public Map getEbsBlockDevices() { return ebsBlockDevices; } 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 d17211567a..b3e743653d 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 @@ -22,6 +22,9 @@ package org.jclouds.ec2.options; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import java.util.Set; + +import org.jclouds.ec2.domain.BlockDeviceMapping; import org.jclouds.ec2.domain.InstanceType; import org.jclouds.ec2.options.internal.BaseEC2RequestOptions; import org.jclouds.encryption.internal.Base64; @@ -33,7 +36,7 @@ import org.jclouds.encryption.internal.Base64; * mutator (if needed): *

* - * import static org.jclouds.ec2.options.RunInstancesOptions.Builder.* + * import static org.jclouds.aws.ec2.options.RunInstancesOptions.Builder.* *

* EC2Client connection = // get connection * Future instances = connection.runInstances(executableBy("123125").imageIds(1000, 1004)); @@ -160,30 +163,6 @@ public class RunInstancesOptions extends BaseEC2RequestOptions { return getFirstFormOrNull("RamdiskId"); } - /** - * The virtual name. - */ - public RunInstancesOptions withVirtualName(String virtualName) { - formParameters.put("BlockDeviceMapping.VirtualName", checkNotNull(virtualName, "virtualName")); - return this; - } - - String getVirtualName() { - return getFirstFormOrNull("BlockDeviceMapping.VirtualName"); - } - - /** - * The device name (e.g., /dev/sdh). - */ - public RunInstancesOptions withDeviceName(String deviceName) { - formParameters.put("BlockDeviceMapping.DeviceName", checkNotNull(deviceName, "deviceName")); - return this; - } - - String getDeviceName() { - return getFirstFormOrNull("BlockDeviceMapping.DeviceName"); - } - /** * Enables monitoring for the instance. */ @@ -208,6 +187,34 @@ public class RunInstancesOptions extends BaseEC2RequestOptions { String getSubnetId() { return getFirstFormOrNull("SubnetId"); } + + /** + * Specifies the Block Device Mapping for the instance + * + */ + + public RunInstancesOptions withBlockDeviceMappings(Set mappings) { + int i = 1; + for(BlockDeviceMapping mapping: mappings) + { + checkNotNull(mapping.getDeviceName(), "deviceName"); + formParameters.put(String.format("BlockDeviceMapping.%d.DeviceName", i), mapping.getDeviceName()); + if(mapping.getVirtualName() != null) + formParameters.put(String.format("BlockDeviceMapping.%d.VirtualName", i), mapping.getVirtualName()); + if(mapping.getEbsSnapshotId() != null) + formParameters.put(String.format("BlockDeviceMapping.%d.Ebs.SnapshotId", i), mapping.getEbsSnapshotId()); + if(mapping.getEbsVolumeSize() != null) + formParameters.put(String.format("BlockDeviceMapping.%d.Ebs.VolumeSize", i), String.valueOf(mapping.getEbsVolumeSize())); + if(mapping.getEbsNoDevice() != null) + formParameters.put(String.format("BlockDeviceMapping.%d.Ebs.NoDevice", i), String.valueOf(mapping.getEbsNoDevice())); + if(mapping.getEbsDeleteOnTermination() != null) + formParameters.put(String.format("BlockDeviceMapping.%d.Ebs.DeleteOnTermination", i), String.valueOf(mapping.getEbsDeleteOnTermination())); + + i++; + } + return this; + } + public static class Builder { /** @@ -258,14 +265,6 @@ public class RunInstancesOptions extends BaseEC2RequestOptions { return options.withKernelId(kernelId); } - /** - * @see RunInstancesOptions#withDeviceName(String) - */ - public static RunInstancesOptions withDeviceName(String deviceName) { - RunInstancesOptions options = new RunInstancesOptions(); - return options.withDeviceName(deviceName); - } - /** * @see RunInstancesOptions#enableMonitoring() */ @@ -289,13 +288,13 @@ public class RunInstancesOptions extends BaseEC2RequestOptions { RunInstancesOptions options = new RunInstancesOptions(); return options.withRamdisk(ramdiskId); } - + /** - * @see RunInstancesOptions#withVirtualName(String) + * @see RunInstancesOptions#withBlockDeviceMappings(Set mappings) */ - public static RunInstancesOptions withVirtualName(String virtualName) { + public static RunInstancesOptions withBlockDeviceMappings(Set mappings) { RunInstancesOptions options = new RunInstancesOptions(); - return options.withVirtualName(virtualName); + return options.withBlockDeviceMappings(mappings); } } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/services/InstanceAsyncClient.java b/apis/ec2/src/main/java/org/jclouds/ec2/services/InstanceAsyncClient.java index 79fcb436b3..cecacd6ecc 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/services/InstanceAsyncClient.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/services/InstanceAsyncClient.java @@ -35,7 +35,7 @@ import org.jclouds.ec2.EC2AsyncClient; import org.jclouds.ec2.binders.BindBlockDeviceMappingToIndexedFormParams; import org.jclouds.ec2.binders.BindInstanceIdsToIndexedFormParams; import org.jclouds.ec2.binders.IfNotNullBindAvailabilityZoneToFormParam; -import org.jclouds.ec2.domain.BlockDeviceMapping; +import org.jclouds.ec2.domain.BlockDevice; import org.jclouds.ec2.domain.InstanceStateChange; import org.jclouds.ec2.domain.Reservation; import org.jclouds.ec2.domain.RunningInstance; @@ -84,8 +84,8 @@ public interface InstanceAsyncClient { @XMLResponseParser(DescribeInstancesResponseHandler.class) @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) ListenableFuture>> describeInstancesInRegion( - @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @BinderParam(BindInstanceIdsToIndexedFormParams.class) String... instanceIds); + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @BinderParam(BindInstanceIdsToIndexedFormParams.class) String... instanceIds); /** * @see InstanceClient#runInstancesInRegion @@ -94,10 +94,11 @@ public interface InstanceAsyncClient { @Path("/") @FormParams(keys = ACTION, values = "RunInstances") @XMLResponseParser(RunInstancesResponseHandler.class) - ListenableFuture> runInstancesInRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @Nullable @BinderParam(IfNotNullBindAvailabilityZoneToFormParam.class) String nullableAvailabilityZone, - @FormParam("ImageId") String imageId, @FormParam("MinCount") int minCount, - @FormParam("MaxCount") int maxCount, RunInstancesOptions... options); + ListenableFuture> runInstancesInRegion( + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @Nullable @BinderParam(IfNotNullBindAvailabilityZoneToFormParam.class) String nullableAvailabilityZone, + @FormParam("ImageId") String imageId, @FormParam("MinCount") int minCount, + @FormParam("MaxCount") int maxCount, RunInstancesOptions... options); /** * @see InstanceClient#rebootInstancesInRegion @@ -105,8 +106,9 @@ public interface InstanceAsyncClient { @POST @Path("/") @FormParams(keys = ACTION, values = "RebootInstances") - ListenableFuture rebootInstancesInRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @BinderParam(BindInstanceIdsToIndexedFormParams.class) String... instanceIds); + ListenableFuture rebootInstancesInRegion( + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @BinderParam(BindInstanceIdsToIndexedFormParams.class) String... instanceIds); /** * @see InstanceClient#terminateInstancesInRegion @@ -116,8 +118,8 @@ public interface InstanceAsyncClient { @FormParams(keys = ACTION, values = "TerminateInstances") @XMLResponseParser(InstanceStateChangeHandler.class) ListenableFuture> terminateInstancesInRegion( - @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @BinderParam(BindInstanceIdsToIndexedFormParams.class) String... instanceIds); + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @BinderParam(BindInstanceIdsToIndexedFormParams.class) String... instanceIds); /** * @see InstanceClient#stopInstancesInRegion @@ -127,8 +129,9 @@ public interface InstanceAsyncClient { @FormParams(keys = ACTION, values = "StopInstances") @XMLResponseParser(InstanceStateChangeHandler.class) ListenableFuture> stopInstancesInRegion( - @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, @FormParam("Force") boolean force, - @BinderParam(BindInstanceIdsToIndexedFormParams.class) String... instanceIds); + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("Force") boolean force, + @BinderParam(BindInstanceIdsToIndexedFormParams.class) String... instanceIds); /** * @see InstanceClient#startInstancesInRegion @@ -138,8 +141,8 @@ public interface InstanceAsyncClient { @FormParams(keys = ACTION, values = "StartInstances") @XMLResponseParser(InstanceStateChangeHandler.class) ListenableFuture> startInstancesInRegion( - @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @BinderParam(BindInstanceIdsToIndexedFormParams.class) String... instanceIds); + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @BinderParam(BindInstanceIdsToIndexedFormParams.class) String... instanceIds); /** * @see AMIClient#getUserDataForInstanceInRegion @@ -149,8 +152,8 @@ public interface InstanceAsyncClient { @FormParams(keys = { ACTION, "Attribute" }, values = { "DescribeInstanceAttribute", "userData" }) @XMLResponseParser(UnencodeStringValueHandler.class) ListenableFuture getUserDataForInstanceInRegion( - @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @FormParam("InstanceId") String instanceId); + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("InstanceId") String instanceId); /** * @see AMIClient#getRootDeviceNameForInstanceInRegion @@ -160,8 +163,8 @@ public interface InstanceAsyncClient { @FormParams(keys = { ACTION, "Attribute" }, values = { "DescribeInstanceAttribute", "rootDeviceName" }) @XMLResponseParser(StringValueHandler.class) ListenableFuture getRootDeviceNameForInstanceInRegion( - @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @FormParam("InstanceId") String instanceId); + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("InstanceId") String instanceId); /** * @see AMIClient#getRamdiskForInstanceInRegion @@ -171,8 +174,8 @@ public interface InstanceAsyncClient { @FormParams(keys = { ACTION, "Attribute" }, values = { "DescribeInstanceAttribute", "ramdisk" }) @XMLResponseParser(StringValueHandler.class) ListenableFuture getRamdiskForInstanceInRegion( - @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @FormParam("InstanceId") String instanceId); + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("InstanceId") String instanceId); /** * @see AMIClient#getKernelForInstanceInRegion @@ -181,8 +184,9 @@ public interface InstanceAsyncClient { @Path("/") @FormParams(keys = { ACTION, "Attribute" }, values = { "DescribeInstanceAttribute", "kernel" }) @XMLResponseParser(StringValueHandler.class) - ListenableFuture getKernelForInstanceInRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @FormParam("InstanceId") String instanceId); + ListenableFuture getKernelForInstanceInRegion( + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("InstanceId") String instanceId); /** * @see AMIClient#isApiTerminationDisabledForInstanceInRegion @@ -192,8 +196,8 @@ public interface InstanceAsyncClient { @FormParams(keys = { ACTION, "Attribute" }, values = { "DescribeInstanceAttribute", "disableApiTermination" }) @XMLResponseParser(BooleanValueHandler.class) ListenableFuture isApiTerminationDisabledForInstanceInRegion( - @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @FormParam("InstanceId") String instanceId); + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("InstanceId") String instanceId); /** * @see AMIClient#getInstanceTypeForInstanceInRegion @@ -203,8 +207,8 @@ public interface InstanceAsyncClient { @FormParams(keys = { ACTION, "Attribute" }, values = { "DescribeInstanceAttribute", "instanceType" }) @XMLResponseParser(InstanceTypeHandler.class) ListenableFuture getInstanceTypeForInstanceInRegion( - @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @FormParam("InstanceId") String instanceId); + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("InstanceId") String instanceId); /** * @see AMIClient#getInstanceInitiatedShutdownBehaviorForInstanceInRegion @@ -212,11 +216,11 @@ public interface InstanceAsyncClient { @POST @Path("/") @FormParams(keys = { ACTION, "Attribute" }, values = { "DescribeInstanceAttribute", - "instanceInitiatedShutdownBehavior" }) + "instanceInitiatedShutdownBehavior" }) @XMLResponseParser(InstanceInitiatedShutdownBehaviorHandler.class) ListenableFuture getInstanceInitiatedShutdownBehaviorForInstanceInRegion( - @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @FormParam("InstanceId") String instanceId); + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("InstanceId") String instanceId); /** * @see InstanceClient#getBlockDeviceMappingForInstanceInRegion @@ -225,9 +229,9 @@ public interface InstanceAsyncClient { @Path("/") @FormParams(keys = { ACTION, "Attribute" }, values = { "DescribeInstanceAttribute", "blockDeviceMapping" }) @XMLResponseParser(BlockDeviceMappingHandler.class) - ListenableFuture> getBlockDeviceMappingForInstanceInRegion( - @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @FormParam("InstanceId") String instanceId); + ListenableFuture> getBlockDeviceMappingForInstanceInRegion( + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("InstanceId") String instanceId); /** * @see AMIClient#resetRamdiskForInstanceInRegion @@ -236,8 +240,8 @@ public interface InstanceAsyncClient { @Path("/") @FormParams(keys = { ACTION, "Attribute" }, values = { "ResetInstanceAttribute", "ramdisk" }) ListenableFuture resetRamdiskForInstanceInRegion( - @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @FormParam("InstanceId") String instanceId); + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("InstanceId") String instanceId); /** * @see AMIClient#resetKernelForInstanceInRegion @@ -245,8 +249,9 @@ public interface InstanceAsyncClient { @POST @Path("/") @FormParams(keys = { ACTION, "Attribute" }, values = { "ResetInstanceAttribute", "kernel" }) - ListenableFuture resetKernelForInstanceInRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @FormParam("InstanceId") String instanceId); + ListenableFuture resetKernelForInstanceInRegion( + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("InstanceId") String instanceId); /** * @see AMIClient#setUserDataForInstanceInRegion @@ -254,9 +259,10 @@ public interface InstanceAsyncClient { @POST @Path("/") @FormParams(keys = { ACTION, "Attribute" }, values = { "ModifyInstanceAttribute", "userData" }) - ListenableFuture setUserDataForInstanceInRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @FormParam("InstanceId") String instanceId, - @FormParam("Value") @ParamParser(ConvertUnencodedBytesToBase64EncodedString.class) byte[] unencodedData); + ListenableFuture setUserDataForInstanceInRegion( + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("InstanceId") String instanceId, + @FormParam("Value") @ParamParser(ConvertUnencodedBytesToBase64EncodedString.class) byte[] unencodedData); /** * @see AMIClient#setRamdiskForInstanceInRegion @@ -264,8 +270,9 @@ public interface InstanceAsyncClient { @POST @Path("/") @FormParams(keys = { ACTION, "Attribute" }, values = { "ModifyInstanceAttribute", "ramdisk" }) - ListenableFuture setRamdiskForInstanceInRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @FormParam("InstanceId") String instanceId, @FormParam("Value") String ramdisk); + ListenableFuture setRamdiskForInstanceInRegion( + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("InstanceId") String instanceId, @FormParam("Value") String ramdisk); /** * @see AMIClient#setKernelForInstanceInRegion @@ -273,8 +280,9 @@ public interface InstanceAsyncClient { @POST @Path("/") @FormParams(keys = { ACTION, "Attribute" }, values = { "ModifyInstanceAttribute", "kernel" }) - ListenableFuture setKernelForInstanceInRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @FormParam("InstanceId") String instanceId, @FormParam("Value") String kernel); + ListenableFuture setKernelForInstanceInRegion( + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("InstanceId") String instanceId, @FormParam("Value") String kernel); /** * @see AMIClient#setApiTerminationDisabledForInstanceInRegion @@ -283,8 +291,8 @@ public interface InstanceAsyncClient { @Path("/") @FormParams(keys = { ACTION, "Attribute" }, values = { "ModifyInstanceAttribute", "disableApiTermination" }) ListenableFuture setApiTerminationDisabledForInstanceInRegion( - @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @FormParam("InstanceId") String instanceId, @FormParam("Value") boolean apiTerminationDisabled); + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("InstanceId") String instanceId, @FormParam("Value") boolean apiTerminationDisabled); /** * @see AMIClient#setInstanceTypeForInstanceInRegion @@ -293,8 +301,8 @@ public interface InstanceAsyncClient { @Path("/") @FormParams(keys = { ACTION, "Attribute" }, values = { "ModifyInstanceAttribute", "instanceType" }) ListenableFuture setInstanceTypeForInstanceInRegion( - @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @FormParam("InstanceId") String instanceId, @FormParam("Value") String instanceType); + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("InstanceId") String instanceId, @FormParam("Value") String instanceType); /** * @see AMIClient#setInstanceInitiatedShutdownBehaviorForInstanceInRegion @@ -302,11 +310,11 @@ public interface InstanceAsyncClient { @POST @Path("/") @FormParams(keys = { ACTION, "Attribute" }, values = { "ModifyInstanceAttribute", - "instanceInitiatedShutdownBehavior" }) + "instanceInitiatedShutdownBehavior" }) ListenableFuture setInstanceInitiatedShutdownBehaviorForInstanceInRegion( - @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @FormParam("InstanceId") String instanceId, - @FormParam("Value") InstanceInitiatedShutdownBehavior instanceInitiatedShutdownBehavior); + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("InstanceId") String instanceId, + @FormParam("Value") InstanceInitiatedShutdownBehavior instanceInitiatedShutdownBehavior); /** * @see InstanceClient#setBlockDeviceMappingForInstanceInRegion @@ -315,8 +323,8 @@ public interface InstanceAsyncClient { @Path("/") @FormParams(keys = { ACTION }, values = { "ModifyInstanceAttribute" }) ListenableFuture setBlockDeviceMappingForInstanceInRegion( - @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @FormParam("InstanceId") String instanceId, - @BinderParam(BindBlockDeviceMappingToIndexedFormParams.class) BlockDeviceMapping blockDeviceMapping); + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("InstanceId") String instanceId, + @BinderParam(BindBlockDeviceMappingToIndexedFormParams.class) Map blockDeviceMapping); } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/services/InstanceClient.java b/apis/ec2/src/main/java/org/jclouds/ec2/services/InstanceClient.java index b7fcdbc1f1..1409549a82 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/services/InstanceClient.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/services/InstanceClient.java @@ -25,11 +25,14 @@ import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; -import org.jclouds.ec2.domain.*; -import org.jclouds.ec2.domain.RunningInstance.EbsBlockDevice; +import org.jclouds.concurrent.Timeout; +import org.jclouds.ec2.domain.BlockDevice; +import org.jclouds.ec2.domain.InstanceState; +import org.jclouds.ec2.domain.InstanceStateChange; +import org.jclouds.ec2.domain.Reservation; +import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.ec2.domain.Volume.InstanceInitiatedShutdownBehavior; import org.jclouds.ec2.options.RunInstancesOptions; -import org.jclouds.concurrent.Timeout; /** * Provides access to EC2 via their REST API. @@ -356,7 +359,7 @@ public interface InstanceClient { * @return Describes the mapping that defines native device names to use when * exposing virtual devices. */ - Map getBlockDeviceMappingForInstanceInRegion( + Map getBlockDeviceMappingForInstanceInRegion( @Nullable String region, String instanceId); /** @@ -536,9 +539,9 @@ public interface InstanceClient { * {@link InstanceState#STOPPING} to {@link InstanceState#STOPPED} * * - * To create the instances of {@link RunningInstance.EbsBlockDevice}, the + * To create the instances of {@link BlockDevice}, the * constructor can be used with the following parameters: - * {@link RunningInstance.EbsBlockDevice#EbsBlockDevice(String, String, boolean)} + * {@link BlockDevice#EbsBlockDevice(String, String, boolean)} * , that are: *

    *
  1. Volume id (required), for instance, "vol-blah"
  2. @@ -571,5 +574,5 @@ public interface InstanceClient { * /> */ void setBlockDeviceMappingForInstanceInRegion(@Nullable String region, - String instanceId, BlockDeviceMapping blockDeviceMapping); + String instanceId, Map blockDeviceMapping); } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/xml/BaseReservationHandler.java b/apis/ec2/src/main/java/org/jclouds/ec2/xml/BaseReservationHandler.java index 84c5181ab6..6a60ae61b2 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/xml/BaseReservationHandler.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/xml/BaseReservationHandler.java @@ -27,6 +27,7 @@ import javax.annotation.Resource; import javax.inject.Inject; import org.jclouds.ec2.domain.Attachment; +import org.jclouds.ec2.domain.BlockDevice; import org.jclouds.ec2.domain.InstanceState; import org.jclouds.ec2.domain.MonitoringState; import org.jclouds.ec2.domain.Reservation; @@ -95,7 +96,7 @@ public abstract class BaseReservationHandler extends HandlerForGeneratedReque protected boolean inMonitoring; private boolean inBlockDeviceMapping; - private Map ebsBlockDevices = Maps.newHashMap(); + private Map ebsBlockDevices = Maps.newHashMap(); private String volumeId; private Attachment.Status attachmentStatus; @@ -229,7 +230,7 @@ public abstract class BaseReservationHandler extends HandlerForGeneratedReque protected void inItem() { if (inBlockDeviceMapping) { - ebsBlockDevices.put(deviceName, new RunningInstance.EbsBlockDevice(volumeId, attachmentStatus, attachTime, + ebsBlockDevices.put(deviceName, new BlockDevice(volumeId, attachmentStatus, attachTime, deleteOnTermination)); this.deviceName = null; this.volumeId = null; diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/xml/BlockDeviceMappingHandler.java b/apis/ec2/src/main/java/org/jclouds/ec2/xml/BlockDeviceMappingHandler.java index 350d362203..a663dc288e 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/xml/BlockDeviceMappingHandler.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/xml/BlockDeviceMappingHandler.java @@ -24,7 +24,7 @@ import java.util.Map; import com.google.inject.Inject; import org.jclouds.ec2.domain.Attachment; -import org.jclouds.ec2.domain.RunningInstance.EbsBlockDevice; +import org.jclouds.ec2.domain.BlockDevice; import org.jclouds.date.DateService; import org.jclouds.http.functions.ParseSax; @@ -35,10 +35,10 @@ import com.google.common.collect.Maps; * @author Adrian Cole */ public class BlockDeviceMappingHandler extends - ParseSax.HandlerWithResult> { + ParseSax.HandlerWithResult> { private StringBuilder currentText = new StringBuilder(); - private Map ebsBlockDevices = Maps.newHashMap(); + private Map ebsBlockDevices = Maps.newHashMap(); private String deviceName; private String volumeId; private boolean deleteOnTermination = true;// correct default is true. @@ -52,7 +52,7 @@ public class BlockDeviceMappingHandler extends this.dateService = dateService; } - public Map getResult() { + public Map getResult() { return ebsBlockDevices; } @@ -68,7 +68,7 @@ public class BlockDeviceMappingHandler extends } else if (qName.equals("attachTime")) { attachTime = dateService.iso8601DateParse(currentText.toString().trim()); } else if (qName.equals("item")) { - ebsBlockDevices.put(deviceName, new EbsBlockDevice(volumeId, attachmentStatus, attachTime, deleteOnTermination)); + ebsBlockDevices.put(deviceName, new BlockDevice(volumeId, attachmentStatus, attachTime, deleteOnTermination)); this.volumeId = null; this.deviceName = null; this.deleteOnTermination = true; diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/CloudApplicationArchitecturesEC2ClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/CloudApplicationArchitecturesEC2ClientLiveTest.java index f89a80285d..fbf64a4cef 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/CloudApplicationArchitecturesEC2ClientLiveTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/CloudApplicationArchitecturesEC2ClientLiveTest.java @@ -28,6 +28,7 @@ import static org.testng.Assert.assertNotNull; import java.io.IOException; import java.net.UnknownHostException; +import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -37,7 +38,7 @@ import java.util.concurrent.TimeoutException; import org.jclouds.Constants; import org.jclouds.aws.AWSResponseException; import org.jclouds.domain.Credentials; -import org.jclouds.ec2.domain.BlockDeviceMapping; +import org.jclouds.ec2.domain.BlockDevice; import org.jclouds.ec2.domain.Image.EbsBlockDevice; import org.jclouds.ec2.domain.InstanceState; import org.jclouds.ec2.domain.InstanceType; @@ -67,6 +68,7 @@ import org.testng.annotations.Test; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; import com.google.inject.Injector; import com.google.inject.Module; @@ -299,9 +301,9 @@ public class CloudApplicationArchitecturesEC2ClientLiveTest { } private void setBlockDeviceMappingForInstanceInRegion() { - BlockDeviceMapping blockDeviceMapping = new BlockDeviceMapping(); + Map mapping = Maps.newLinkedHashMap(); try { - client.getInstanceServices().setBlockDeviceMappingForInstanceInRegion(null, instanceId, blockDeviceMapping); + client.getInstanceServices().setBlockDeviceMappingForInstanceInRegion(null, instanceId, mapping); assert false : "shouldn't be allowed, as instance needs to be ebs based-ami"; } catch (AWSResponseException e) { assertEquals("InvalidParameterCombination", e.getError().getCode()); diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java index d8b0439a2d..24af71a4cb 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java @@ -39,7 +39,7 @@ import org.jclouds.Constants; import org.jclouds.aws.AWSResponseException; import org.jclouds.domain.Credentials; import org.jclouds.ec2.domain.Attachment; -import org.jclouds.ec2.domain.BlockDeviceMapping; +import org.jclouds.ec2.domain.BlockDevice; import org.jclouds.ec2.domain.Image; import org.jclouds.ec2.domain.Image.Architecture; import org.jclouds.ec2.domain.Image.ImageType; @@ -83,6 +83,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; import com.google.inject.Injector; import com.google.inject.Module; @@ -474,17 +475,16 @@ public class EBSBootEC2ClientLiveTest { private void setBlockDeviceMappingForInstanceInRegion() { String volumeId = ebsInstance.getEbsBlockDevices().get("/dev/sda1").getVolumeId(); - BlockDeviceMapping blockDeviceMapping = new BlockDeviceMapping(); - blockDeviceMapping.addEbsBlockDevice("/dev/sda1", new RunningInstance.EbsBlockDevice(volumeId, false)); + Map mapping = Maps.newLinkedHashMap(); + mapping.put("/dev/sda1", new BlockDevice(volumeId, false)); try { - client.getInstanceServices().setBlockDeviceMappingForInstanceInRegion(null, ebsInstance.getId(), - blockDeviceMapping); + client.getInstanceServices().setBlockDeviceMappingForInstanceInRegion(null, ebsInstance.getId(), mapping); - Map devices = client.getInstanceServices() - .getBlockDeviceMappingForInstanceInRegion(null, ebsInstance.getId()); + Map devices = client.getInstanceServices().getBlockDeviceMappingForInstanceInRegion(null, + ebsInstance.getId()); assertEquals(devices.size(), 1); String deviceName = Iterables.getOnlyElement(devices.keySet()); - RunningInstance.EbsBlockDevice device = Iterables.getOnlyElement(devices.values()); + BlockDevice device = Iterables.getOnlyElement(devices.values()); assertEquals(device.getVolumeId(), volumeId); assertEquals(deviceName, "/dev/sda1"); diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/binders/BindBlockDeviceMappingToIndexedFormParamsTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/binders/BindBlockDeviceMappingToIndexedFormParamsTest.java index e44b786861..36b7554011 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/binders/BindBlockDeviceMappingToIndexedFormParamsTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/binders/BindBlockDeviceMappingToIndexedFormParamsTest.java @@ -24,15 +24,16 @@ import static org.testng.Assert.assertEquals; import java.io.File; import java.net.URI; import java.util.Date; +import java.util.Map; import javax.ws.rs.HttpMethod; import org.jclouds.ec2.domain.Attachment.Status; -import org.jclouds.ec2.domain.BlockDeviceMapping; -import org.jclouds.ec2.domain.RunningInstance; +import org.jclouds.ec2.domain.BlockDevice; import org.jclouds.http.HttpRequest; import org.testng.annotations.Test; +import com.google.common.collect.Maps; import com.google.inject.Guice; import com.google.inject.Injector; @@ -48,11 +49,10 @@ public class BindBlockDeviceMappingToIndexedFormParamsTest { .getInstance(BindBlockDeviceMappingToIndexedFormParams.class); public void testMapping() { - BlockDeviceMapping mapping = new BlockDeviceMapping(); - mapping.addEbsBlockDevice("apple", new RunningInstance.EbsBlockDevice("appleId", true)); + Map mapping = Maps.newLinkedHashMap(); + mapping.put("apple", new BlockDevice("appleId", true)); Date date = new Date(999999l); - mapping.addEbsBlockDevice("cranberry", new RunningInstance.EbsBlockDevice("cranberry", Status.ATTACHED, date, - false)); + mapping.put("cranberry", new BlockDevice("cranberry", Status.ATTACHED, date, false)); HttpRequest request = HttpRequest.builder().method("POST").endpoint(URI.create("http://localhost")).build(); request = binder.bindToRequest(request, mapping); diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java index 0afc3b10a9..203fb0b4ec 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java @@ -41,6 +41,7 @@ import org.jclouds.ec2.compute.functions.CreatePlacementGroupIfNeeded; import org.jclouds.ec2.compute.functions.CreateSecurityGroupIfNeeded; import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair; import org.jclouds.ec2.compute.options.EC2TemplateOptions; +import org.jclouds.ec2.domain.BlockDeviceMapping; import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.PlacementGroup; import org.jclouds.ec2.options.RunInstancesOptions; @@ -86,6 +87,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT // setup expectations expect(template.getHardware()).andReturn(size).atLeastOnce(); expect(template.getOptions()).andReturn(options).atLeastOnce(); + expect(options.getBlockDeviceMappings()).andReturn(ImmutableSet. of()).atLeastOnce(); expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options)).andReturn( systemGeneratedKeyPairName); expect(strategy.getSecurityGroupsForTagAndOptions(region, tag, options)).andReturn(generatedGroups); @@ -142,6 +144,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT // setup expectations expect(template.getHardware()).andReturn(size).atLeastOnce(); expect(template.getOptions()).andReturn(options).atLeastOnce(); + expect(options.getBlockDeviceMappings()).andReturn(ImmutableSet. of()).atLeastOnce(); expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options)).andReturn( systemGeneratedKeyPairName); expect(strategy.createNewPlacementGroupUnlessUserSpecifiedOtherwise(region, tag, options)).andReturn( @@ -201,6 +204,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT // setup expectations expect(template.getHardware()).andReturn(size).atLeastOnce(); expect(template.getOptions()).andReturn(options).atLeastOnce(); + expect(options.getBlockDeviceMappings()).andReturn(ImmutableSet. of()).atLeastOnce(); expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options)).andReturn( systemGeneratedKeyPairName); expect(strategy.createNewPlacementGroupUnlessUserSpecifiedOtherwise(region, tag, options)).andReturn( @@ -258,6 +262,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT // setup expectations expect(template.getHardware()).andReturn(size).atLeastOnce(); expect(template.getOptions()).andReturn(options).atLeastOnce(); + expect(options.getBlockDeviceMappings()).andReturn(ImmutableSet. of()).atLeastOnce(); expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options)).andReturn( systemGeneratedKeyPairName); expect(options.getSubnetId()).andReturn("1"); @@ -313,6 +318,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT // setup expectations expect(template.getHardware()).andReturn(size).atLeastOnce(); expect(template.getOptions()).andReturn(options).atLeastOnce(); + expect(options.getBlockDeviceMappings()).andReturn(ImmutableSet. of()).atLeastOnce(); expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options)).andReturn( systemGeneratedKeyPairName); expect(strategy.getSecurityGroupsForTagAndOptions(region, tag, options)).andReturn(generatedGroups); 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 829094cbb0..e94200e161 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 @@ -21,18 +21,20 @@ package org.jclouds.ec2.options; import static org.jclouds.ec2.options.RunInstancesOptions.Builder.asType; import static org.jclouds.ec2.options.RunInstancesOptions.Builder.enableMonitoring; -import static org.jclouds.ec2.options.RunInstancesOptions.Builder.withDeviceName; import static org.jclouds.ec2.options.RunInstancesOptions.Builder.withKernelId; import static org.jclouds.ec2.options.RunInstancesOptions.Builder.withKeyName; import static org.jclouds.ec2.options.RunInstancesOptions.Builder.withRamdisk; import static org.jclouds.ec2.options.RunInstancesOptions.Builder.withSecurityGroup; import static org.jclouds.ec2.options.RunInstancesOptions.Builder.withSubnetId; import static org.jclouds.ec2.options.RunInstancesOptions.Builder.withUserData; -import static org.jclouds.ec2.options.RunInstancesOptions.Builder.withVirtualName; +import static org.jclouds.ec2.options.RunInstancesOptions.Builder.withBlockDeviceMappings; import static org.testng.Assert.assertEquals; import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import org.jclouds.ec2.domain.BlockDeviceMapping; import org.jclouds.ec2.domain.InstanceType; import org.jclouds.http.options.HttpRequestOptions; import org.testng.annotations.Test; @@ -181,32 +183,6 @@ public class RunInstancesOptionsTest { withKernelId(null); } - @Test - public void testWithDeviceName() { - RunInstancesOptions options = new RunInstancesOptions(); - options.withDeviceName("test"); - assertEquals(options.buildFormParameters().get("BlockDeviceMapping.DeviceName"), - Collections.singletonList("test")); - } - - @Test - public void testNullWithDeviceName() { - RunInstancesOptions options = new RunInstancesOptions(); - assertEquals(options.buildFormParameters().get("BlockDeviceMapping.DeviceName"), Collections.EMPTY_LIST); - } - - @Test - public void testWithDeviceNameStatic() { - RunInstancesOptions options = withDeviceName("test"); - assertEquals(options.buildFormParameters().get("BlockDeviceMapping.DeviceName"), - Collections.singletonList("test")); - } - - @Test(expectedExceptions = NullPointerException.class) - public void testWithDeviceNameNPE() { - withDeviceName(null); - } - @Test public void testWithMonitoringEnabled() { RunInstancesOptions options = new RunInstancesOptions(); @@ -274,30 +250,44 @@ public class RunInstancesOptionsTest { withRamdisk(null); } - @Test - public void testWithVirtualName() { - RunInstancesOptions options = new RunInstancesOptions(); - options.withVirtualName("test"); - assertEquals(options.buildFormParameters().get("BlockDeviceMapping.VirtualName"), - Collections.singletonList("test")); - } - @Test public void testNullWithVirtualName() { RunInstancesOptions options = new RunInstancesOptions(); assertEquals(options.buildFormParameters().get("BlockDeviceMapping.VirtualName"), Collections.EMPTY_LIST); } + + @Test + public void testWithBlockDeviceMapping() { + RunInstancesOptions options = new RunInstancesOptions(); + BlockDeviceMapping mapping = new BlockDeviceMapping("/dev/sda1", null, null, 120, null, true); + Set mappings = new HashSet(); + mappings.add(mapping); + options.withBlockDeviceMappings(mappings); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.DeviceName"), Collections.singletonList("/dev/sda1")); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.Ebs.VolumeSize"), Collections.singletonList("120")); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.Ebs.DeleteOnTermination"), Collections.singletonList("true")); + } @Test - public void testWithVirtualNameStatic() { - RunInstancesOptions options = withVirtualName("test"); - assertEquals(options.buildFormParameters().get("BlockDeviceMapping.VirtualName"), - Collections.singletonList("test")); + public void testNullWithBlockDeviceMapping() { + RunInstancesOptions options = new RunInstancesOptions(); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping"), Collections.EMPTY_LIST); + } + + @Test + public void testWithBlockDeviceMappingStatic() { + BlockDeviceMapping mapping = new BlockDeviceMapping("/dev/sda1", null, null, 120, null, true); + Set mappings = new HashSet(); + mappings.add(mapping); + RunInstancesOptions options = withBlockDeviceMappings(mappings); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.DeviceName"), Collections.singletonList("/dev/sda1")); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.Ebs.VolumeSize"), Collections.singletonList("120")); + assertEquals(options.buildFormParameters().get("BlockDeviceMapping.1.Ebs.DeleteOnTermination"), Collections.singletonList("true")); } @Test(expectedExceptions = NullPointerException.class) - public void testWithVirtualNameNPE() { - withVirtualName(null); + public void testWithBlockDeviceMappingNPE() { + withBlockDeviceMappings(null); } } diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/InstanceAsyncClientTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/InstanceAsyncClientTest.java index 19e54b9266..6fdbbf9a5f 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/services/InstanceAsyncClientTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/InstanceAsyncClientTest.java @@ -22,12 +22,12 @@ package org.jclouds.ec2.services; import java.io.IOException; import java.lang.reflect.Array; import java.lang.reflect.Method; +import java.util.Map; import org.jclouds.aws.domain.Region; import org.jclouds.ec2.domain.AvailabilityZone; -import org.jclouds.ec2.domain.BlockDeviceMapping; +import org.jclouds.ec2.domain.BlockDevice; import org.jclouds.ec2.domain.InstanceType; -import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.ec2.domain.Volume.InstanceInitiatedShutdownBehavior; import org.jclouds.ec2.options.RunInstancesOptions; import org.jclouds.ec2.xml.BlockDeviceMappingHandler; @@ -46,6 +46,7 @@ import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; import org.jclouds.rest.internal.RestAnnotationProcessor; import org.testng.annotations.Test; +import com.google.common.collect.Maps; import com.google.inject.TypeLiteral; /** @@ -468,11 +469,11 @@ public class InstanceAsyncClientTest extends BaseEC2AsyncClientTest mapping = Maps.newLinkedHashMap(); + mapping.put("/dev/sda1", new BlockDevice("vol-test1", true)); + HttpRequest request = processor.createRequest(method, null, "1", mapping); assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/xml/BlockDeviceMappingHandlerTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/xml/BlockDeviceMappingHandlerTest.java index 4ac10d2924..e6e60c2608 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/xml/BlockDeviceMappingHandlerTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/xml/BlockDeviceMappingHandlerTest.java @@ -25,7 +25,7 @@ import java.io.InputStream; import java.util.Map; import org.jclouds.ec2.domain.Attachment; -import org.jclouds.ec2.domain.RunningInstance.EbsBlockDevice; +import org.jclouds.ec2.domain.BlockDevice; import org.jclouds.date.DateService; import org.jclouds.http.functions.BaseHandlerTest; import org.testng.annotations.Test; @@ -46,13 +46,13 @@ public class BlockDeviceMappingHandlerTest extends BaseHandlerTest { "/describe_image_attribute_blockDeviceMapping.xml"); DateService dateService = injector.getInstance(DateService.class); - Map expected = ImmutableMap. of("/dev/sda1", - new EbsBlockDevice("vol-d74b82be", Attachment.Status.ATTACHED, dateService + Map expected = ImmutableMap. of("/dev/sda1", + new BlockDevice("vol-d74b82be", Attachment.Status.ATTACHED, dateService .iso8601DateParse("2010-02-20T18:25:26.000Z"), true), "/dev/sdf", - new EbsBlockDevice("vol-another", Attachment.Status.DETACHED, dateService + new BlockDevice("vol-another", Attachment.Status.DETACHED, dateService .iso8601DateParse("2010-02-20T19:26:26.000Z"), false)); - Map result = factory.create( + Map result = factory.create( injector.getInstance(BlockDeviceMappingHandler.class)).parse(is); assertEquals(result, expected); diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeInstancesResponseHandlerTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeInstancesResponseHandlerTest.java index 4759ef0337..170caedd8c 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeInstancesResponseHandlerTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeInstancesResponseHandlerTest.java @@ -27,13 +27,13 @@ import java.util.Set; import org.jclouds.ec2.domain.Attachment; import org.jclouds.ec2.domain.AvailabilityZone; +import org.jclouds.ec2.domain.BlockDevice; import org.jclouds.ec2.domain.InstanceState; import org.jclouds.ec2.domain.InstanceType; import org.jclouds.ec2.domain.MonitoringState; import org.jclouds.ec2.domain.Reservation; import org.jclouds.ec2.domain.RootDeviceType; import org.jclouds.ec2.domain.RunningInstance; -import org.jclouds.ec2.domain.RunningInstance.EbsBlockDevice; import org.jclouds.date.DateService; import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.config.SaxParserModule; @@ -77,7 +77,7 @@ public class DescribeInstancesResponseHandlerTest extends BaseEC2HandlerTest { .iso8601DateParse("2009-11-09T03:00:34.000Z"), MonitoringState.DISABLED, AvailabilityZone.US_EAST_1C, null, "paravirtual", null, "ip-10-243-42-70.ec2.internal", "10.243.42.70", ImmutableSet. of(), "ari-a51cf9cc", null, null, null, null, - RootDeviceType.INSTANCE_STORE, null, ImmutableMap. of())), "993194456877", + RootDeviceType.INSTANCE_STORE, null, ImmutableMap. of())), "993194456877", null, "r-a3c508cb")); Set> result = parseRunningInstances("/describe_instances_running.xml"); @@ -96,14 +96,14 @@ public class DescribeInstancesResponseHandlerTest extends BaseEC2HandlerTest { .iso8601DateParse("2007-08-07T11:54:42.000Z"), MonitoringState.DISABLED, AvailabilityZone.US_EAST_1B, null, "paravirtual", null, "10-251-50-132.ec2.internal", null, ImmutableSet.of("774F4FF8"), "ari-badbad00", null, null, null, null, RootDeviceType.INSTANCE_STORE, - null, ImmutableMap. of()), + null, ImmutableMap. of()), new RunningInstance(defaultRegion, ImmutableSet.of("default"), "23", "ec2-72-44-33-6.compute-1.amazonaws.com", "ami-6ea54007", "i-28a64435", InstanceState.RUNNING, InstanceType.M1_LARGE, (String) null, "aki-ba3adfd3", "example-key-name", dateService .iso8601DateParse("2007-08-07T11:54:42.000Z"), MonitoringState.DISABLED, AvailabilityZone.US_EAST_1B, null, "paravirtual", null, "10-251-50-134.ec2.internal", null, ImmutableSet.of("774F4FF8"), "ari-badbad00", null, null, null, null, RootDeviceType.INSTANCE_STORE, - null, ImmutableMap. of())), "UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM", null, + null, ImmutableMap. of())), "UYY3TLBUXIEON5NQVUUX6OMPWBZIQNFM", null, "r-44a5402d")); Set> result = parseRunningInstances("/describe_instances.xml"); @@ -123,7 +123,7 @@ public class DescribeInstancesResponseHandlerTest extends BaseEC2HandlerTest { "jclouds#euc-17", dateService.iso8601DateParse("2010-06-16T03:06:19.000Z"), MonitoringState.DISABLED, "open", null, "paravirtual", null, "10.7.0.179", null, ImmutableSet. of(), "eri-A97113E4", null, null, null, null, RootDeviceType.INSTANCE_STORE, null, ImmutableMap - . of())), "jclouds", null, "r-4D2A08AD")); + . of())), "jclouds", null, "r-4D2A08AD")); Set> result = parseRunningInstances("/describe_instances_euc.xml"); @@ -138,7 +138,7 @@ public class DescribeInstancesResponseHandlerTest extends BaseEC2HandlerTest { InstanceState.TERMINATED, InstanceType.M1_SMALL, null, null, "nebulatanimislam", dateService .iso8601SecondsDateParse("2010-09-09T18:09:42Z"), null, null, null, "paravirtual", null, null, "10.128.207.5", ImmutableSet. of("None"), null, null, null, null, null, - RootDeviceType.INSTANCE_STORE, null, ImmutableMap. of())), "tislam1", null, + RootDeviceType.INSTANCE_STORE, null, ImmutableMap. of())), "tislam1", null, "r-opqeylmj")); Set> result = parseRunningInstances("/describe_instances_nova.xml"); @@ -157,9 +157,9 @@ public class DescribeInstancesResponseHandlerTest extends BaseEC2HandlerTest { .iso8601DateParse("2009-12-30T04:06:23.000Z"), MonitoringState.DISABLED, AvailabilityZone.US_EAST_1B, "placement", "hvm", null, "domU-12-31-39-09-CE-53.compute-1.internal", "10.210.209.157", ImmutableSet. of(), "ari-a51cf9cc", null, null, null, null, - RootDeviceType.EBS, "/dev/sda1", ImmutableMap. of( + RootDeviceType.EBS, "/dev/sda1", ImmutableMap. of( "/dev/sda1", - new EbsBlockDevice("vol-dc6ca8b5", Attachment.Status.ATTACHED, dateService + new BlockDevice("vol-dc6ca8b5", Attachment.Status.ATTACHED, dateService .iso8601DateParse("2009-12-30T04:06:29.000Z"), true)))), "993194456877", null, "r-596dd731")); diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/xml/RunInstancesResponseHandlerTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/xml/RunInstancesResponseHandlerTest.java index bcb60baac8..9988a14e38 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/xml/RunInstancesResponseHandlerTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/xml/RunInstancesResponseHandlerTest.java @@ -27,13 +27,13 @@ import static org.testng.Assert.assertEquals; import java.io.InputStream; import org.jclouds.ec2.domain.AvailabilityZone; +import org.jclouds.ec2.domain.BlockDevice; import org.jclouds.ec2.domain.InstanceState; import org.jclouds.ec2.domain.InstanceType; import org.jclouds.ec2.domain.MonitoringState; import org.jclouds.ec2.domain.Reservation; import org.jclouds.ec2.domain.RootDeviceType; import org.jclouds.ec2.domain.RunningInstance; -import org.jclouds.ec2.domain.RunningInstance.EbsBlockDevice; import org.jclouds.date.DateService; import org.jclouds.http.functions.ParseSax; import org.jclouds.rest.internal.GeneratedHttpRequest; @@ -74,17 +74,17 @@ public class RunInstancesResponseHandlerTest extends BaseEC2HandlerTest { "example-key-name", dateService.iso8601DateParse("2007-08-07T11:51:50.000Z"), MonitoringState.ENABLED, AvailabilityZone.US_EAST_1B, null, "paravirtual", null, (String) null, null, Sets . newLinkedHashSet(), null, null, null, null, null, RootDeviceType.INSTANCE_STORE, - null, ImmutableMap. of()), new RunningInstance(defaultRegion, ImmutableSet + null, ImmutableMap. of()), new RunningInstance(defaultRegion, ImmutableSet .of("default"), "1", null, "ami-60a54009", "i-2bc64242", InstanceState.PENDING, InstanceType.M1_SMALL, (String) null, null, "example-key-name", dateService.iso8601DateParse("2007-08-07T11:51:50.000Z"), MonitoringState.ENABLED, AvailabilityZone.US_EAST_1B, null, "paravirtual", null, (String) null, null, Sets. newLinkedHashSet(), null, null, null, null, null, RootDeviceType.INSTANCE_STORE, null, - ImmutableMap. of()), new RunningInstance(defaultRegion, ImmutableSet + ImmutableMap. of()), new RunningInstance(defaultRegion, ImmutableSet .of("default"), "2", null, "ami-60a54009", "i-2be64332", InstanceState.PENDING, InstanceType.M1_SMALL, (String) null, null, "example-key-name", dateService.iso8601DateParse("2007-08-07T11:51:50.000Z"), MonitoringState.ENABLED, AvailabilityZone.US_EAST_1B, null, "paravirtual", null, (String) null, null, Sets. newLinkedHashSet(), null, null, null, null, null, RootDeviceType.INSTANCE_STORE, null, - ImmutableMap. of()) + ImmutableMap. of()) ), "AIDADH4IGTRXXKCD", null, "r-47a5402e"); diff --git a/aws/core/src/main/java/org/jclouds/aws/ec2/options/BlockDeviceMapping.java b/aws/core/src/main/java/org/jclouds/aws/ec2/options/BlockDeviceMapping.java new file mode 100644 index 0000000000..b6fa5ec0a7 --- /dev/null +++ b/aws/core/src/main/java/org/jclouds/aws/ec2/options/BlockDeviceMapping.java @@ -0,0 +1,158 @@ +package org.jclouds.aws.ec2.options; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.annotation.Nullable; + +import org.jclouds.util.Preconditions2; + +public class BlockDeviceMapping +{ + private final String deviceName; + private final String virtualName; + private final String snapshotId; + private final Integer sizeInGib; + private final Boolean noDevice; + private final Boolean deleteOnTermination; + + // values expressed in GB + private static final Integer VOLUME_SIZE_MIN_VALUE = 1; + private static final Integer VOLUME_SIZE_MAX_VALUE = 1000; + + public BlockDeviceMapping(String deviceName, @Nullable String virtualName, + @Nullable String snapshotId, @Nullable Integer sizeInGib, + @Nullable Boolean noDevice, @Nullable Boolean deleteOnTermination) + { + + checkNotNull(deviceName, "deviceName cannot be null"); + Preconditions2.checkNotEmpty(deviceName, + "the deviceName must be non-empty"); + + if (sizeInGib != null) + { + checkArgument( + (sizeInGib >= VOLUME_SIZE_MIN_VALUE && sizeInGib <= VOLUME_SIZE_MAX_VALUE), + String.format("Size in Gib must be between %s and %s GB", + VOLUME_SIZE_MIN_VALUE, VOLUME_SIZE_MAX_VALUE)); + } + this.deviceName = deviceName; + this.virtualName = virtualName; + this.snapshotId = snapshotId; + this.sizeInGib = sizeInGib; + this.noDevice = noDevice; + this.deleteOnTermination = deleteOnTermination; + } + + public String getDeviceName() + { + return deviceName; + } + + public String getVirtualName() + { + return virtualName; + } + + public String getEbsSnapshotId() + { + return snapshotId; + } + + public Integer getEbsVolumeSize() + { + return sizeInGib; + } + + public Boolean getEbsNoDevice() + { + return noDevice; + } + + public Boolean getEbsDeleteOnTermination() + { + return deleteOnTermination; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + + ((deviceName == null) ? 0 : deviceName.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; + BlockDeviceMapping other = (BlockDeviceMapping) obj; + if (deviceName == null) + { + if (other.deviceName != null) + return false; + } + else if (!deviceName.equals(other.deviceName)) + return false; + return true; + } + + @Override + public String toString() + { + return "BlockDeviceMapping [deviceName=" + deviceName + + ", virtualName=" + virtualName + ", snapshotId=" + snapshotId + + ", sizeInGib=" + sizeInGib + ", noDevice=" + noDevice + + ", deleteOnTermination=" + deleteOnTermination + "]"; + } + + 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); + checkNotNull(snapshotId, "snapshotId cannot be null"); + Preconditions2.checkNotEmpty(snapshotId, + "the snapshotId must be non-empty"); + } + } + + public static class MapNewVolumeToDevice extends BlockDeviceMapping + { + public MapNewVolumeToDevice(String deviceName, Integer sizeInGib, + @Nullable Boolean deleteOnTermination) + { + super(deviceName, null, null, sizeInGib, null, deleteOnTermination); + 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); + checkNotNull(virtualName, "virtualName cannot be null"); + Preconditions2.checkNotEmpty(virtualName, + "the virtualName must be non-empty"); + } + } + + public static class UnmapDeviceNamed extends BlockDeviceMapping + { + public UnmapDeviceNamed(String deviceName) + { + super(deviceName, null, null, null, true, null); + } + } +} \ No newline at end of file diff --git a/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilder.java b/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilder.java index c9329e5e45..b8f07f9e3f 100644 --- a/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilder.java +++ b/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilder.java @@ -78,7 +78,7 @@ public interface TemplateBuilder { * Configure this template to start in a specific location * * @throws NoSuchElementException - * if location matches the id specified + * if no location matches the id specified */ TemplateBuilder locationId(String locationId);