EC2: Add iops to volume (#5776)

This commit is contained in:
Hisashi Kamezawa 2022-12-16 12:22:55 -08:00 committed by GitHub
parent e5d40f63f8
commit 52891e1641
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 6 deletions

View File

@ -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

View File

@ -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

View File

@ -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 = """<CreateVolumeResponse xmlns="http://ec2.amazonaws.co
{% endif %}
<availabilityZone>{{ volume.zone.name }}</availabilityZone>
<status>creating</status>
<createTime>{{ volume.create_time}}</createTime>
<createTime>{{ volume.create_time }}</createTime>
{% if volume.get_tags() %}
<tagSet>
{% for tag in volume.get_tags() %}
@ -223,6 +225,9 @@ CREATE_VOLUME_RESPONSE = """<CreateVolumeResponse xmlns="http://ec2.amazonaws.co
</tagSet>
{% endif %}
<volumeType>{{ volume.volume_type }}</volumeType>
{% if volume.iops %}
<iops>{{ volume.iops }}</iops>
{% endif %}
</CreateVolumeResponse>"""
DESCRIBE_VOLUMES_RESPONSE = """<DescribeVolumesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
@ -243,7 +248,7 @@ DESCRIBE_VOLUMES_RESPONSE = """<DescribeVolumesResponse xmlns="http://ec2.amazon
{% endif %}
<availabilityZone>{{ volume.zone.name }}</availabilityZone>
<status>{{ volume.status }}</status>
<createTime>{{ volume.create_time}}</createTime>
<createTime>{{ volume.create_time }}</createTime>
<attachmentSet>
{% if volume.attachment %}
<item>
@ -269,6 +274,9 @@ DESCRIBE_VOLUMES_RESPONSE = """<DescribeVolumesResponse xmlns="http://ec2.amazon
</tagSet>
{% endif %}
<volumeType>{{ volume.volume_type }}</volumeType>
{% if volume.iops %}
<iops>{{ volume.iops }}</iops>
{% endif %}
</item>
{% endfor %}
</volumeSet>

View File

@ -5,6 +5,7 @@ import sure # noqa # pylint: disable=unused-import
from botocore.exceptions import ClientError
from moto import mock_ec2
from moto.core import DEFAULT_ACCOUNT_ID as OWNER_ID
from moto.ec2.models.elastic_block_store import IOPS_REQUIRED_VOLUME_TYPES
from moto.kms import mock_kms
from tests import EXAMPLE_AMI_ID
from uuid import uuid4
@ -925,7 +926,7 @@ def test_kms_key_id_property_hidden_when_volume_not_encrypted():
@mock_ec2
def test_create_volume_with_standard_type():
ec2 = boto3.client("ec2", region_name="us-east-1")
volume = ec2.create_volume(AvailabilityZone="us-east-1a", Size=100, Iops=1000)
volume = ec2.create_volume(AvailabilityZone="us-east-1a", Size=100)
volume["VolumeType"].should.equal("gp2")
volume = ec2.describe_volumes(VolumeIds=[volume["VolumeId"]])["Volumes"][0]
@ -936,9 +937,14 @@ def test_create_volume_with_standard_type():
@mock_ec2
def test_create_volume_with_non_standard_type(volume_type):
ec2 = boto3.client("ec2", region_name="us-east-1")
volume = ec2.create_volume(
AvailabilityZone="us-east-1a", Size=100, Iops=1000, VolumeType=volume_type
)
if volume_type in IOPS_REQUIRED_VOLUME_TYPES:
volume = ec2.create_volume(
AvailabilityZone="us-east-1a", Size=100, Iops=3000, VolumeType=volume_type
)
else:
volume = ec2.create_volume(
AvailabilityZone="us-east-1a", Size=100, VolumeType=volume_type
)
volume["VolumeType"].should.equal(volume_type)
volume = ec2.describe_volumes(VolumeIds=[volume["VolumeId"]])["Volumes"][0]
@ -1083,3 +1089,15 @@ def test_create_snapshots_multiple_volumes_without_boot():
snapshot2 = next(s for s in snapshots if s["VolumeId"] == volume2.volume_id)
snapshot2.should.have.key("VolumeSize").equals(100)
@mock_ec2
def test_create_volume_with_iops():
ec2 = boto3.client("ec2", region_name="us-east-1")
volume = ec2.create_volume(
AvailabilityZone="us-east-1a", Size=10, VolumeType="gp3", Iops=4000
)
volume["Iops"].should.equal(4000)
volume = ec2.describe_volumes(VolumeIds=[volume["VolumeId"]])["Volumes"][0]
volume["Iops"].should.equal(4000)