diff --git a/moto/ec2/models/elastic_block_store.py b/moto/ec2/models/elastic_block_store.py index 44833edf1..33148d4bd 100644 --- a/moto/ec2/models/elastic_block_store.py +++ b/moto/ec2/models/elastic_block_store.py @@ -19,6 +19,10 @@ from ..utils import ( utc_date_and_time, ) +IOPS_REQUIRED_VOLUME_TYPES = ["io1", "io2"] +IOPS_SUPPORTED_VOLUME_TYPES = ["gp3", "io1", "io2"] +GP3_DEFAULT_IOPS = 3000 + class VolumeModification(object): def __init__(self, volume, target_size=None, target_volume_type=None): @@ -97,6 +101,7 @@ class Volume(TaggedEC2Resource, CloudFormationModel): encrypted=False, kms_key_id=None, volume_type=None, + iops=None, ): self.id = volume_id self.volume_type = volume_type or "gp2" @@ -109,6 +114,7 @@ class Volume(TaggedEC2Resource, CloudFormationModel): self.encrypted = encrypted self.kms_key_id = kms_key_id self.modifications = [] + self.iops = iops def modify(self, target_size=None, target_volume_type=None): modification = VolumeModification( @@ -240,11 +246,19 @@ class EBSBackend: encrypted=False, kms_key_id=None, volume_type=None, + iops=None, ): if kms_key_id and not encrypted: raise InvalidParameterDependency("KmsKeyId", "Encrypted") if encrypted and not kms_key_id: kms_key_id = self._get_default_encryption_key() + if volume_type in IOPS_REQUIRED_VOLUME_TYPES and not iops: + raise InvalidParameterDependency("VolumeType", "Iops") + elif volume_type == "gp3" and not iops: + iops = GP3_DEFAULT_IOPS + elif volume_type not in IOPS_SUPPORTED_VOLUME_TYPES and iops: + raise InvalidParameterDependency("VolumeType", "Iops") + volume_id = random_volume_id() zone = self.get_zone_by_name(zone_name) if snapshot_id: @@ -262,6 +276,7 @@ class EBSBackend: encrypted=encrypted, kms_key_id=kms_key_id, volume_type=volume_type, + iops=iops, ) self.volumes[volume_id] = volume return volume diff --git a/moto/ec2/models/instances.py b/moto/ec2/models/instances.py index f4be1beed..6094773fd 100644 --- a/moto/ec2/models/instances.py +++ b/moto/ec2/models/instances.py @@ -199,6 +199,7 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel): delete_on_termination=False, kms_key_id=None, volume_type=None, + iops=None, ): volume = self.ec2_backend.create_volume( size=size, @@ -207,6 +208,7 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel): encrypted=encrypted, kms_key_id=kms_key_id, volume_type=volume_type, + iops=iops, ) self.ec2_backend.attach_volume( volume.id, self.id, device_path, delete_on_termination diff --git a/moto/ec2/responses/elastic_block_store.py b/moto/ec2/responses/elastic_block_store.py index d99b8eed0..71729f143 100644 --- a/moto/ec2/responses/elastic_block_store.py +++ b/moto/ec2/responses/elastic_block_store.py @@ -61,6 +61,7 @@ class ElasticBlockStore(EC2BaseResponse): volume_tags = tags.get("volume", {}) encrypted = self._get_bool_param("Encrypted", if_none=False) kms_key_id = self._get_param("KmsKeyId") + iops = self._get_param("Iops") if self.is_not_dryrun("CreateVolume"): volume = self.ec2_backend.create_volume( size=size, @@ -69,6 +70,7 @@ class ElasticBlockStore(EC2BaseResponse): encrypted=encrypted, kms_key_id=kms_key_id, volume_type=volume_type, + iops=iops, ) volume.add_tags(volume_tags) template = self.response_template(CREATE_VOLUME_RESPONSE) @@ -209,7 +211,7 @@ CREATE_VOLUME_RESPONSE = """ @@ -243,7 +248,7 @@ DESCRIBE_VOLUMES_RESPONSE = """