EC2: Support all (current) instance-type filters (#5526)
This commit is contained in:
parent
b8932b19c9
commit
148de0e562
@ -427,8 +427,8 @@ class InvalidServiceName(EC2ClientError):
|
|||||||
|
|
||||||
|
|
||||||
class InvalidFilter(EC2ClientError):
|
class InvalidFilter(EC2ClientError):
|
||||||
def __init__(self, filter_name):
|
def __init__(self, filter_name, error_type="InvalidFilter"):
|
||||||
super().__init__("InvalidFilter", f"The filter '{filter_name}' is invalid")
|
super().__init__(error_type, f"The filter '{filter_name}' is invalid")
|
||||||
|
|
||||||
|
|
||||||
class InvalidNextToken(EC2ClientError):
|
class InvalidNextToken(EC2ClientError):
|
||||||
|
@ -4,7 +4,7 @@ from os import listdir
|
|||||||
from ..utils import generic_filter
|
from ..utils import generic_filter
|
||||||
|
|
||||||
from moto.utilities.utils import load_resource
|
from moto.utilities.utils import load_resource
|
||||||
from ..exceptions import FilterNotImplementedError, InvalidInstanceTypeError
|
from ..exceptions import InvalidFilter, InvalidInstanceTypeError
|
||||||
|
|
||||||
INSTANCE_TYPES = load_resource(__name__, "../resources/instance_types.json")
|
INSTANCE_TYPES = load_resource(__name__, "../resources/instance_types.json")
|
||||||
INSTANCE_FAMILIES = list(set([i.split(".")[0] for i in INSTANCE_TYPES.keys()]))
|
INSTANCE_FAMILIES = list(set([i.split(".")[0] for i in INSTANCE_TYPES.keys()]))
|
||||||
@ -23,6 +23,87 @@ for location_type in listdir(root / offerings_path):
|
|||||||
|
|
||||||
|
|
||||||
class InstanceType(dict):
|
class InstanceType(dict):
|
||||||
|
_filter_attributes = {
|
||||||
|
"auto-recovery-supported": ["AutoRecoverySupported"],
|
||||||
|
"bare-metal": ["BareMetal"],
|
||||||
|
"burstable-performance-supported": ["BurstablePerformanceSupported"],
|
||||||
|
"current-generation": ["CurrentGeneration"],
|
||||||
|
"ebs-info.ebs-optimized-info.baseline-bandwidth-in-mbps": [
|
||||||
|
"EbsInfo", "EbsOptimizedInfo", "BaselineBandwidthInMbps"
|
||||||
|
],
|
||||||
|
"ebs-info.ebs-optimized-info.baseline-iops": [
|
||||||
|
"EbsInfo", "EbsOptimizedInfo", "BaselineIops"
|
||||||
|
],
|
||||||
|
"ebs-info.ebs-optimized-info.baseline-throughput-in-mbps": [
|
||||||
|
"EbsInfo", "EbsOptimizedInfo", "BaselineThroughputInMBps"
|
||||||
|
],
|
||||||
|
"ebs-info.ebs-optimized-info.maximum-bandwidth-in-mbps": [
|
||||||
|
"EbsInfo", "EbsOptimizedInfo", "MaximumBandwidthInMbps"
|
||||||
|
],
|
||||||
|
"ebs-info.ebs-optimized-info.maximum-iops": [
|
||||||
|
"EbsInfo", "EbsOptimizedInfo", "MaximumIops"
|
||||||
|
],
|
||||||
|
"ebs-info.ebs-optimized-info.maximum-throughput-in-mbps": [
|
||||||
|
"EbsInfo", "EbsOptimizedInfo", "MaximumThroughputInMBps"
|
||||||
|
],
|
||||||
|
"ebs-info.ebs-optimized-support": ["EbsInfo", "EbsOptimizedSupport"],
|
||||||
|
"ebs-info.encryption-support": ["EbsInfo", "EncryptionSupport"],
|
||||||
|
"ebs-info.nvme-support": ["EbsInfo", "NvmeSupport"],
|
||||||
|
"free-tier-eligible": ["FreeTierEligible"],
|
||||||
|
"hibernation-supported": ["HibernationSupported"],
|
||||||
|
"hypervisor": ["Hypervisor"],
|
||||||
|
"instance-storage-info.disk.count": ["InstanceStorageInfo", "Disks", "Count"],
|
||||||
|
"instance-storage-info.disk.size-in-gb": [
|
||||||
|
"InstanceStorageInfo", "Disks", "SizeInGB"
|
||||||
|
],
|
||||||
|
"instance-storage-info.disk.type": ["InstanceStorageInfo", "Disks", "Type"],
|
||||||
|
"instance-storage-info.encryption-support": [
|
||||||
|
"InstanceStorageInfo", "EncryptionSupport"
|
||||||
|
],
|
||||||
|
"instance-storage-info.nvme-support": ["InstanceStorageInfo", "NvmeSupport"],
|
||||||
|
"instance-storage-info.total-size-in-gb": [
|
||||||
|
"InstanceStorageInfo", "TotalSizeInGB"
|
||||||
|
],
|
||||||
|
"instance-storage-supported": ["InstanceStorageSupported"],
|
||||||
|
"instance-type": ["InstanceType"],
|
||||||
|
"memory-info.size-in-mib": ["MemoryInfo", "SizeInMiB"],
|
||||||
|
"network-info.efa-info.maximum-efa-interfaces": [
|
||||||
|
"NetworkInfo", "EfaInfo", "MaximumEfaInterfaces"
|
||||||
|
],
|
||||||
|
"network-info.efa-supported": ["NetworkInfo", "EfaSupported"],
|
||||||
|
"network-info.ena-support": ["NetworkInfo", "EnaSupport"],
|
||||||
|
"network-info.encryption-in-transit-supported": [
|
||||||
|
"NetworkInfo", "EncryptionInTransitSupported"
|
||||||
|
],
|
||||||
|
"network-info.ipv4-addresses-per-interface": [
|
||||||
|
"NetworkInfo", "Ipv4AddressesPerInterface"
|
||||||
|
],
|
||||||
|
"network-info.ipv6-addresses-per-interface": [
|
||||||
|
"NetworkInfo", "Ipv6AddressesPerInterface"
|
||||||
|
],
|
||||||
|
"network-info.ipv6-supported": ["NetworkInfo", "Ipv6Supported"],
|
||||||
|
"network-info.maximum-network-cards": ["NetworkInfo", "MaximumNetworkCards"],
|
||||||
|
"network-info.maximum-network-interfaces": [
|
||||||
|
"NetworkInfo", "MaximumNetworkInterfaces"
|
||||||
|
],
|
||||||
|
"network-info.network-performance": ["NetworkInfo", "NetworkPerformance"],
|
||||||
|
"processor-info.supported-architecture": [
|
||||||
|
"ProcessorInfo", "SupportedArchitectures"
|
||||||
|
],
|
||||||
|
"processor-info.sustained-clock-speed-in-ghz": [
|
||||||
|
"ProcessorInfo", "SustainedClockSpeedInGhz"
|
||||||
|
],
|
||||||
|
"supported-boot-mode": ["SupportedBootModes"],
|
||||||
|
"supported-root-device-type": ["SupportedRootDeviceTypes"],
|
||||||
|
"supported-usage-class": ["SupportedUsageClasses"],
|
||||||
|
"supported-virtualization-type": ["SupportedVirtualizationTypes"],
|
||||||
|
"vcpu-info.default-cores": ["VCpuInfo", "DefaultCores"],
|
||||||
|
"vcpu-info.default-threads-per-core": ["VCpuInfo", "DefaultThreadsPerCore"],
|
||||||
|
"vcpu-info.default-vcpus": ["VCpuInfo", "DefaultVCpus"],
|
||||||
|
"vcpu-info.valid-cores": ["VCpuInfo", "ValidCores"],
|
||||||
|
"vcpu-info.valid-threads-per-core": ["VCpuInfo", "ValidThreadsPerCore"],
|
||||||
|
} # fmt: skip
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.update(INSTANCE_TYPES[name])
|
self.update(INSTANCE_TYPES[name])
|
||||||
@ -37,20 +118,20 @@ class InstanceType(dict):
|
|||||||
return "<InstanceType: %s>" % self.name
|
return "<InstanceType: %s>" % self.name
|
||||||
|
|
||||||
def get_filter_value(self, filter_name):
|
def get_filter_value(self, filter_name):
|
||||||
if filter_name in ("instance-type"):
|
def stringify(v):
|
||||||
return self.get("InstanceType")
|
if isinstance(v, (bool, int)):
|
||||||
elif filter_name in ("vcpu-info.default-vcpus"):
|
return str(v).lower()
|
||||||
return str(self.get("VCpuInfo").get("DefaultVCpus"))
|
elif isinstance(v, list):
|
||||||
elif filter_name in ("memory-info.size-in-mib"):
|
return [stringify(i) for i in v]
|
||||||
return str(self.get("MemoryInfo").get("SizeInMiB"))
|
return v
|
||||||
elif filter_name in ("bare-metal"):
|
|
||||||
return str(self.get("BareMetal")).lower()
|
path = self._filter_attributes.get(filter_name)
|
||||||
elif filter_name in ("burstable-performance-supported"):
|
if not path:
|
||||||
return str(self.get("BurstablePerformanceSupported")).lower()
|
raise InvalidFilter(filter_name, error_type="InvalidParameterValue")
|
||||||
elif filter_name in ("current-generation"):
|
value = self
|
||||||
return str(self.get("CurrentGeneration")).lower()
|
for key in path:
|
||||||
else:
|
value = (value or {}).get(key)
|
||||||
return FilterNotImplementedError(filter_name, "DescribeInstanceTypes")
|
return stringify(value)
|
||||||
|
|
||||||
|
|
||||||
class InstanceTypeBackend:
|
class InstanceTypeBackend:
|
||||||
|
@ -180,3 +180,34 @@ def test_describe_instance_types_filter_by_current_generation():
|
|||||||
|
|
||||||
# not contain
|
# not contain
|
||||||
types.should_not.contain("t1.micro")
|
types.should_not.contain("t1.micro")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_describe_instance_types_small_instances():
|
||||||
|
client = boto3.client("ec2", "us-east-1")
|
||||||
|
instance_types = client.describe_instance_types(Filters=[
|
||||||
|
{"Name": "bare-metal", "Values": ["false"]},
|
||||||
|
{"Name": "current-generation", "Values": ["true"]},
|
||||||
|
{"Name": "vcpu-info.default-cores", "Values": ["1"]},
|
||||||
|
{"Name": "memory-info.size-in-mib", "Values": ["512", "1024"]},
|
||||||
|
{"Name": "vcpu-info.valid-threads-per-core", "Values": ["1"]},
|
||||||
|
]) # fmt: skip
|
||||||
|
|
||||||
|
types = set(t["InstanceType"] for t in instance_types["InstanceTypes"])
|
||||||
|
types.should.equal({"t3.nano", "t3.micro", "t3a.nano", "t3a.micro"})
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_describe_instance_types_invalid_filter():
|
||||||
|
client = boto3.client("ec2", "us-east-1")
|
||||||
|
|
||||||
|
with pytest.raises(ClientError) as exc_info:
|
||||||
|
client.describe_instance_types(
|
||||||
|
Filters=[{"Name": "spam", "Values": ["eggs"]}],
|
||||||
|
)
|
||||||
|
|
||||||
|
exc_info.value.response["Error"]["Code"].should.equal("InvalidParameterValue")
|
||||||
|
exc_info.value.response["Error"]["Message"].split(":")[0].should.equal(
|
||||||
|
"The filter 'spam' is invalid",
|
||||||
|
)
|
||||||
|
exc_info.value.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
|
||||||
|
Loading…
Reference in New Issue
Block a user