Merge pull request #2764 from bblommers/feature/ec2-describe-instance-filters
Feature - EC2 describe_instance_status now uses filters
This commit is contained in:
commit
a92f862e86
@ -822,6 +822,21 @@ class Instance(TaggedEC2Resource, BotoInstance):
|
|||||||
return self.public_ip
|
return self.public_ip
|
||||||
raise UnformattedGetAttTemplateException()
|
raise UnformattedGetAttTemplateException()
|
||||||
|
|
||||||
|
def applies(self, filters):
|
||||||
|
if filters:
|
||||||
|
applicable = False
|
||||||
|
for f in filters:
|
||||||
|
acceptable_values = f["values"]
|
||||||
|
if f["name"] == "instance-state-name":
|
||||||
|
if self._state.name in acceptable_values:
|
||||||
|
applicable = True
|
||||||
|
if f["name"] == "instance-state-code":
|
||||||
|
if str(self._state.code) in acceptable_values:
|
||||||
|
applicable = True
|
||||||
|
return applicable
|
||||||
|
# If there are no filters, all instances are valid
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class InstanceBackend(object):
|
class InstanceBackend(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -921,22 +936,23 @@ class InstanceBackend(object):
|
|||||||
value = getattr(instance, key)
|
value = getattr(instance, key)
|
||||||
return instance, value
|
return instance, value
|
||||||
|
|
||||||
def all_instances(self):
|
def all_instances(self, filters=None):
|
||||||
instances = []
|
instances = []
|
||||||
for reservation in self.all_reservations():
|
for reservation in self.all_reservations():
|
||||||
for instance in reservation.instances:
|
for instance in reservation.instances:
|
||||||
instances.append(instance)
|
if instance.applies(filters):
|
||||||
return instances
|
|
||||||
|
|
||||||
def all_running_instances(self):
|
|
||||||
instances = []
|
|
||||||
for reservation in self.all_reservations():
|
|
||||||
for instance in reservation.instances:
|
|
||||||
if instance.state_code == 16:
|
|
||||||
instances.append(instance)
|
instances.append(instance)
|
||||||
return instances
|
return instances
|
||||||
|
|
||||||
def get_multi_instances_by_id(self, instance_ids):
|
def all_running_instances(self, filters=None):
|
||||||
|
instances = []
|
||||||
|
for reservation in self.all_reservations():
|
||||||
|
for instance in reservation.instances:
|
||||||
|
if instance.state_code == 16 and instance.applies(filters):
|
||||||
|
instances.append(instance)
|
||||||
|
return instances
|
||||||
|
|
||||||
|
def get_multi_instances_by_id(self, instance_ids, filters=None):
|
||||||
"""
|
"""
|
||||||
:param instance_ids: A string list with instance ids
|
:param instance_ids: A string list with instance ids
|
||||||
:return: A list with instance objects
|
:return: A list with instance objects
|
||||||
@ -946,7 +962,8 @@ class InstanceBackend(object):
|
|||||||
for reservation in self.all_reservations():
|
for reservation in self.all_reservations():
|
||||||
for instance in reservation.instances:
|
for instance in reservation.instances:
|
||||||
if instance.id in instance_ids:
|
if instance.id in instance_ids:
|
||||||
result.append(instance)
|
if instance.applies(filters):
|
||||||
|
result.append(instance)
|
||||||
|
|
||||||
# TODO: Trim error message down to specific invalid id.
|
# TODO: Trim error message down to specific invalid id.
|
||||||
if instance_ids and len(instance_ids) > len(result):
|
if instance_ids and len(instance_ids) > len(result):
|
||||||
|
@ -113,16 +113,34 @@ class InstanceResponse(BaseResponse):
|
|||||||
template = self.response_template(EC2_START_INSTANCES)
|
template = self.response_template(EC2_START_INSTANCES)
|
||||||
return template.render(instances=instances)
|
return template.render(instances=instances)
|
||||||
|
|
||||||
|
def _get_list_of_dict_params(self, param_prefix, _dct):
|
||||||
|
"""
|
||||||
|
Simplified version of _get_dict_param
|
||||||
|
Allows you to pass in a custom dict instead of using self.querystring by default
|
||||||
|
"""
|
||||||
|
params = []
|
||||||
|
for key, value in _dct.items():
|
||||||
|
if key.startswith(param_prefix):
|
||||||
|
params.append(value)
|
||||||
|
return params
|
||||||
|
|
||||||
def describe_instance_status(self):
|
def describe_instance_status(self):
|
||||||
instance_ids = self._get_multi_param("InstanceId")
|
instance_ids = self._get_multi_param("InstanceId")
|
||||||
include_all_instances = self._get_param("IncludeAllInstances") == "true"
|
include_all_instances = self._get_param("IncludeAllInstances") == "true"
|
||||||
|
filters = self._get_list_prefix("Filter")
|
||||||
|
filters = [
|
||||||
|
{"name": f["name"], "values": self._get_list_of_dict_params("value.", f)}
|
||||||
|
for f in filters
|
||||||
|
]
|
||||||
|
|
||||||
if instance_ids:
|
if instance_ids:
|
||||||
instances = self.ec2_backend.get_multi_instances_by_id(instance_ids)
|
instances = self.ec2_backend.get_multi_instances_by_id(
|
||||||
|
instance_ids, filters
|
||||||
|
)
|
||||||
elif include_all_instances:
|
elif include_all_instances:
|
||||||
instances = self.ec2_backend.all_instances()
|
instances = self.ec2_backend.all_instances(filters)
|
||||||
else:
|
else:
|
||||||
instances = self.ec2_backend.all_running_instances()
|
instances = self.ec2_backend.all_running_instances(filters)
|
||||||
|
|
||||||
template = self.response_template(EC2_INSTANCE_STATUS)
|
template = self.response_template(EC2_INSTANCE_STATUS)
|
||||||
return template.render(instances=instances)
|
return template.render(instances=instances)
|
||||||
|
@ -1144,7 +1144,7 @@ def test_describe_instance_status_with_instances():
|
|||||||
|
|
||||||
|
|
||||||
@mock_ec2_deprecated
|
@mock_ec2_deprecated
|
||||||
def test_describe_instance_status_with_instance_filter():
|
def test_describe_instance_status_with_instance_filter_deprecated():
|
||||||
conn = boto.connect_ec2("the_key", "the_secret")
|
conn = boto.connect_ec2("the_key", "the_secret")
|
||||||
|
|
||||||
# We want to filter based on this one
|
# We want to filter based on this one
|
||||||
@ -1166,6 +1166,75 @@ def test_describe_instance_status_with_instance_filter():
|
|||||||
cm.exception.request_id.should_not.be.none
|
cm.exception.request_id.should_not.be.none
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_describe_instance_status_with_instance_filter():
|
||||||
|
conn = boto3.client("ec2", region_name="us-west-1")
|
||||||
|
|
||||||
|
# We want to filter based on this one
|
||||||
|
reservation = conn.run_instances(ImageId="ami-1234abcd", MinCount=3, MaxCount=3)
|
||||||
|
instance1 = reservation["Instances"][0]
|
||||||
|
instance2 = reservation["Instances"][1]
|
||||||
|
instance3 = reservation["Instances"][2]
|
||||||
|
conn.stop_instances(InstanceIds=[instance1["InstanceId"]])
|
||||||
|
stopped_instance_ids = [instance1["InstanceId"]]
|
||||||
|
running_instance_ids = sorted([instance2["InstanceId"], instance3["InstanceId"]])
|
||||||
|
all_instance_ids = sorted(stopped_instance_ids + running_instance_ids)
|
||||||
|
|
||||||
|
# Filter instance using the state name
|
||||||
|
state_name_filter = {
|
||||||
|
"running_and_stopped": [
|
||||||
|
{"Name": "instance-state-name", "Values": ["running", "stopped"]}
|
||||||
|
],
|
||||||
|
"running": [{"Name": "instance-state-name", "Values": ["running"]}],
|
||||||
|
"stopped": [{"Name": "instance-state-name", "Values": ["stopped"]}],
|
||||||
|
}
|
||||||
|
|
||||||
|
found_statuses = conn.describe_instance_status(
|
||||||
|
IncludeAllInstances=True, Filters=state_name_filter["running_and_stopped"]
|
||||||
|
)["InstanceStatuses"]
|
||||||
|
found_instance_ids = [status["InstanceId"] for status in found_statuses]
|
||||||
|
sorted(found_instance_ids).should.equal(all_instance_ids)
|
||||||
|
|
||||||
|
found_statuses = conn.describe_instance_status(
|
||||||
|
IncludeAllInstances=True, Filters=state_name_filter["running"]
|
||||||
|
)["InstanceStatuses"]
|
||||||
|
found_instance_ids = [status["InstanceId"] for status in found_statuses]
|
||||||
|
sorted(found_instance_ids).should.equal(running_instance_ids)
|
||||||
|
|
||||||
|
found_statuses = conn.describe_instance_status(
|
||||||
|
IncludeAllInstances=True, Filters=state_name_filter["stopped"]
|
||||||
|
)["InstanceStatuses"]
|
||||||
|
found_instance_ids = [status["InstanceId"] for status in found_statuses]
|
||||||
|
sorted(found_instance_ids).should.equal(stopped_instance_ids)
|
||||||
|
|
||||||
|
# Filter instance using the state code
|
||||||
|
state_code_filter = {
|
||||||
|
"running_and_stopped": [
|
||||||
|
{"Name": "instance-state-code", "Values": ["16", "80"]}
|
||||||
|
],
|
||||||
|
"running": [{"Name": "instance-state-code", "Values": ["16"]}],
|
||||||
|
"stopped": [{"Name": "instance-state-code", "Values": ["80"]}],
|
||||||
|
}
|
||||||
|
|
||||||
|
found_statuses = conn.describe_instance_status(
|
||||||
|
IncludeAllInstances=True, Filters=state_code_filter["running_and_stopped"]
|
||||||
|
)["InstanceStatuses"]
|
||||||
|
found_instance_ids = [status["InstanceId"] for status in found_statuses]
|
||||||
|
sorted(found_instance_ids).should.equal(all_instance_ids)
|
||||||
|
|
||||||
|
found_statuses = conn.describe_instance_status(
|
||||||
|
IncludeAllInstances=True, Filters=state_code_filter["running"]
|
||||||
|
)["InstanceStatuses"]
|
||||||
|
found_instance_ids = [status["InstanceId"] for status in found_statuses]
|
||||||
|
sorted(found_instance_ids).should.equal(running_instance_ids)
|
||||||
|
|
||||||
|
found_statuses = conn.describe_instance_status(
|
||||||
|
IncludeAllInstances=True, Filters=state_code_filter["stopped"]
|
||||||
|
)["InstanceStatuses"]
|
||||||
|
found_instance_ids = [status["InstanceId"] for status in found_statuses]
|
||||||
|
sorted(found_instance_ids).should.equal(stopped_instance_ids)
|
||||||
|
|
||||||
|
|
||||||
@requires_boto_gte("2.32.0")
|
@requires_boto_gte("2.32.0")
|
||||||
@mock_ec2_deprecated
|
@mock_ec2_deprecated
|
||||||
def test_describe_instance_status_with_non_running_instances():
|
def test_describe_instance_status_with_non_running_instances():
|
||||||
|
Loading…
Reference in New Issue
Block a user