EC2: Add iops to volume (#5776)
This commit is contained in:
parent
e5d40f63f8
commit
52891e1641
@ -19,6 +19,10 @@ from ..utils import (
|
|||||||
utc_date_and_time,
|
utc_date_and_time,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
IOPS_REQUIRED_VOLUME_TYPES = ["io1", "io2"]
|
||||||
|
IOPS_SUPPORTED_VOLUME_TYPES = ["gp3", "io1", "io2"]
|
||||||
|
GP3_DEFAULT_IOPS = 3000
|
||||||
|
|
||||||
|
|
||||||
class VolumeModification(object):
|
class VolumeModification(object):
|
||||||
def __init__(self, volume, target_size=None, target_volume_type=None):
|
def __init__(self, volume, target_size=None, target_volume_type=None):
|
||||||
@ -97,6 +101,7 @@ class Volume(TaggedEC2Resource, CloudFormationModel):
|
|||||||
encrypted=False,
|
encrypted=False,
|
||||||
kms_key_id=None,
|
kms_key_id=None,
|
||||||
volume_type=None,
|
volume_type=None,
|
||||||
|
iops=None,
|
||||||
):
|
):
|
||||||
self.id = volume_id
|
self.id = volume_id
|
||||||
self.volume_type = volume_type or "gp2"
|
self.volume_type = volume_type or "gp2"
|
||||||
@ -109,6 +114,7 @@ class Volume(TaggedEC2Resource, CloudFormationModel):
|
|||||||
self.encrypted = encrypted
|
self.encrypted = encrypted
|
||||||
self.kms_key_id = kms_key_id
|
self.kms_key_id = kms_key_id
|
||||||
self.modifications = []
|
self.modifications = []
|
||||||
|
self.iops = iops
|
||||||
|
|
||||||
def modify(self, target_size=None, target_volume_type=None):
|
def modify(self, target_size=None, target_volume_type=None):
|
||||||
modification = VolumeModification(
|
modification = VolumeModification(
|
||||||
@ -240,11 +246,19 @@ class EBSBackend:
|
|||||||
encrypted=False,
|
encrypted=False,
|
||||||
kms_key_id=None,
|
kms_key_id=None,
|
||||||
volume_type=None,
|
volume_type=None,
|
||||||
|
iops=None,
|
||||||
):
|
):
|
||||||
if kms_key_id and not encrypted:
|
if kms_key_id and not encrypted:
|
||||||
raise InvalidParameterDependency("KmsKeyId", "Encrypted")
|
raise InvalidParameterDependency("KmsKeyId", "Encrypted")
|
||||||
if encrypted and not kms_key_id:
|
if encrypted and not kms_key_id:
|
||||||
kms_key_id = self._get_default_encryption_key()
|
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()
|
volume_id = random_volume_id()
|
||||||
zone = self.get_zone_by_name(zone_name)
|
zone = self.get_zone_by_name(zone_name)
|
||||||
if snapshot_id:
|
if snapshot_id:
|
||||||
@ -262,6 +276,7 @@ class EBSBackend:
|
|||||||
encrypted=encrypted,
|
encrypted=encrypted,
|
||||||
kms_key_id=kms_key_id,
|
kms_key_id=kms_key_id,
|
||||||
volume_type=volume_type,
|
volume_type=volume_type,
|
||||||
|
iops=iops,
|
||||||
)
|
)
|
||||||
self.volumes[volume_id] = volume
|
self.volumes[volume_id] = volume
|
||||||
return volume
|
return volume
|
||||||
|
@ -199,6 +199,7 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
|
|||||||
delete_on_termination=False,
|
delete_on_termination=False,
|
||||||
kms_key_id=None,
|
kms_key_id=None,
|
||||||
volume_type=None,
|
volume_type=None,
|
||||||
|
iops=None,
|
||||||
):
|
):
|
||||||
volume = self.ec2_backend.create_volume(
|
volume = self.ec2_backend.create_volume(
|
||||||
size=size,
|
size=size,
|
||||||
@ -207,6 +208,7 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
|
|||||||
encrypted=encrypted,
|
encrypted=encrypted,
|
||||||
kms_key_id=kms_key_id,
|
kms_key_id=kms_key_id,
|
||||||
volume_type=volume_type,
|
volume_type=volume_type,
|
||||||
|
iops=iops,
|
||||||
)
|
)
|
||||||
self.ec2_backend.attach_volume(
|
self.ec2_backend.attach_volume(
|
||||||
volume.id, self.id, device_path, delete_on_termination
|
volume.id, self.id, device_path, delete_on_termination
|
||||||
|
@ -61,6 +61,7 @@ class ElasticBlockStore(EC2BaseResponse):
|
|||||||
volume_tags = tags.get("volume", {})
|
volume_tags = tags.get("volume", {})
|
||||||
encrypted = self._get_bool_param("Encrypted", if_none=False)
|
encrypted = self._get_bool_param("Encrypted", if_none=False)
|
||||||
kms_key_id = self._get_param("KmsKeyId")
|
kms_key_id = self._get_param("KmsKeyId")
|
||||||
|
iops = self._get_param("Iops")
|
||||||
if self.is_not_dryrun("CreateVolume"):
|
if self.is_not_dryrun("CreateVolume"):
|
||||||
volume = self.ec2_backend.create_volume(
|
volume = self.ec2_backend.create_volume(
|
||||||
size=size,
|
size=size,
|
||||||
@ -69,6 +70,7 @@ class ElasticBlockStore(EC2BaseResponse):
|
|||||||
encrypted=encrypted,
|
encrypted=encrypted,
|
||||||
kms_key_id=kms_key_id,
|
kms_key_id=kms_key_id,
|
||||||
volume_type=volume_type,
|
volume_type=volume_type,
|
||||||
|
iops=iops,
|
||||||
)
|
)
|
||||||
volume.add_tags(volume_tags)
|
volume.add_tags(volume_tags)
|
||||||
template = self.response_template(CREATE_VOLUME_RESPONSE)
|
template = self.response_template(CREATE_VOLUME_RESPONSE)
|
||||||
@ -209,7 +211,7 @@ CREATE_VOLUME_RESPONSE = """<CreateVolumeResponse xmlns="http://ec2.amazonaws.co
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<availabilityZone>{{ volume.zone.name }}</availabilityZone>
|
<availabilityZone>{{ volume.zone.name }}</availabilityZone>
|
||||||
<status>creating</status>
|
<status>creating</status>
|
||||||
<createTime>{{ volume.create_time}}</createTime>
|
<createTime>{{ volume.create_time }}</createTime>
|
||||||
{% if volume.get_tags() %}
|
{% if volume.get_tags() %}
|
||||||
<tagSet>
|
<tagSet>
|
||||||
{% for tag in volume.get_tags() %}
|
{% for tag in volume.get_tags() %}
|
||||||
@ -223,6 +225,9 @@ CREATE_VOLUME_RESPONSE = """<CreateVolumeResponse xmlns="http://ec2.amazonaws.co
|
|||||||
</tagSet>
|
</tagSet>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<volumeType>{{ volume.volume_type }}</volumeType>
|
<volumeType>{{ volume.volume_type }}</volumeType>
|
||||||
|
{% if volume.iops %}
|
||||||
|
<iops>{{ volume.iops }}</iops>
|
||||||
|
{% endif %}
|
||||||
</CreateVolumeResponse>"""
|
</CreateVolumeResponse>"""
|
||||||
|
|
||||||
DESCRIBE_VOLUMES_RESPONSE = """<DescribeVolumesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
|
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 %}
|
{% endif %}
|
||||||
<availabilityZone>{{ volume.zone.name }}</availabilityZone>
|
<availabilityZone>{{ volume.zone.name }}</availabilityZone>
|
||||||
<status>{{ volume.status }}</status>
|
<status>{{ volume.status }}</status>
|
||||||
<createTime>{{ volume.create_time}}</createTime>
|
<createTime>{{ volume.create_time }}</createTime>
|
||||||
<attachmentSet>
|
<attachmentSet>
|
||||||
{% if volume.attachment %}
|
{% if volume.attachment %}
|
||||||
<item>
|
<item>
|
||||||
@ -269,6 +274,9 @@ DESCRIBE_VOLUMES_RESPONSE = """<DescribeVolumesResponse xmlns="http://ec2.amazon
|
|||||||
</tagSet>
|
</tagSet>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<volumeType>{{ volume.volume_type }}</volumeType>
|
<volumeType>{{ volume.volume_type }}</volumeType>
|
||||||
|
{% if volume.iops %}
|
||||||
|
<iops>{{ volume.iops }}</iops>
|
||||||
|
{% endif %}
|
||||||
</item>
|
</item>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</volumeSet>
|
</volumeSet>
|
||||||
|
@ -5,6 +5,7 @@ import sure # noqa # pylint: disable=unused-import
|
|||||||
from botocore.exceptions import ClientError
|
from botocore.exceptions import ClientError
|
||||||
from moto import mock_ec2
|
from moto import mock_ec2
|
||||||
from moto.core import DEFAULT_ACCOUNT_ID as OWNER_ID
|
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 moto.kms import mock_kms
|
||||||
from tests import EXAMPLE_AMI_ID
|
from tests import EXAMPLE_AMI_ID
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
@ -925,7 +926,7 @@ def test_kms_key_id_property_hidden_when_volume_not_encrypted():
|
|||||||
@mock_ec2
|
@mock_ec2
|
||||||
def test_create_volume_with_standard_type():
|
def test_create_volume_with_standard_type():
|
||||||
ec2 = boto3.client("ec2", region_name="us-east-1")
|
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["VolumeType"].should.equal("gp2")
|
||||||
|
|
||||||
volume = ec2.describe_volumes(VolumeIds=[volume["VolumeId"]])["Volumes"][0]
|
volume = ec2.describe_volumes(VolumeIds=[volume["VolumeId"]])["Volumes"][0]
|
||||||
@ -936,9 +937,14 @@ def test_create_volume_with_standard_type():
|
|||||||
@mock_ec2
|
@mock_ec2
|
||||||
def test_create_volume_with_non_standard_type(volume_type):
|
def test_create_volume_with_non_standard_type(volume_type):
|
||||||
ec2 = boto3.client("ec2", region_name="us-east-1")
|
ec2 = boto3.client("ec2", region_name="us-east-1")
|
||||||
volume = ec2.create_volume(
|
if volume_type in IOPS_REQUIRED_VOLUME_TYPES:
|
||||||
AvailabilityZone="us-east-1a", Size=100, Iops=1000, VolumeType=volume_type
|
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["VolumeType"].should.equal(volume_type)
|
||||||
|
|
||||||
volume = ec2.describe_volumes(VolumeIds=[volume["VolumeId"]])["Volumes"][0]
|
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 = next(s for s in snapshots if s["VolumeId"] == volume2.volume_id)
|
||||||
snapshot2.should.have.key("VolumeSize").equals(100)
|
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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user