Add ec2 filtering by instance state
This commit is contained in:
parent
76ea9172da
commit
257ca9f771
4
moto/ec2/exceptions.py
Normal file
4
moto/ec2/exceptions.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
class InvalidIdError(RuntimeError):
|
||||||
|
def __init__(self, instance_id):
|
||||||
|
super(InvalidIdError, self).__init__()
|
||||||
|
self.instance_id = instance_id
|
@ -4,6 +4,7 @@ from collections import defaultdict
|
|||||||
from boto.ec2.instance import Instance as BotoInstance, Reservation
|
from boto.ec2.instance import Instance as BotoInstance, Reservation
|
||||||
|
|
||||||
from moto.core import BaseBackend
|
from moto.core import BaseBackend
|
||||||
|
from .exceptions import InvalidIdError
|
||||||
from .utils import (
|
from .utils import (
|
||||||
random_ami_id,
|
random_ami_id,
|
||||||
random_instance_id,
|
random_instance_id,
|
||||||
@ -137,6 +138,10 @@ class InstanceBackend(object):
|
|||||||
reservation_copy = copy.deepcopy(reservation)
|
reservation_copy = copy.deepcopy(reservation)
|
||||||
reservation_copy.instances = [instance for instance in reservation_copy.instances if instance.id in instance_ids]
|
reservation_copy.instances = [instance for instance in reservation_copy.instances if instance.id in instance_ids]
|
||||||
reservations.append(reservation_copy)
|
reservations.append(reservation_copy)
|
||||||
|
found_instance_ids = [instance.id for reservation in reservations for instance in reservation.instances]
|
||||||
|
if len(found_instance_ids) != len(instance_ids):
|
||||||
|
invalid_id = list(set(instance_ids).difference(set(found_instance_ids)))[0]
|
||||||
|
raise InvalidIdError(invalid_id)
|
||||||
return reservations
|
return reservations
|
||||||
|
|
||||||
def all_reservations(self):
|
def all_reservations(self):
|
||||||
|
@ -2,17 +2,26 @@ from jinja2 import Template
|
|||||||
|
|
||||||
from moto.core.utils import camelcase_to_underscores
|
from moto.core.utils import camelcase_to_underscores
|
||||||
from moto.ec2.models import ec2_backend
|
from moto.ec2.models import ec2_backend
|
||||||
from moto.ec2.utils import instance_ids_from_querystring
|
from moto.ec2.utils import instance_ids_from_querystring, filters_from_querystring, filter_reservations
|
||||||
|
from moto.ec2.exceptions import InvalidIdError
|
||||||
|
|
||||||
|
|
||||||
class InstanceResponse(object):
|
class InstanceResponse(object):
|
||||||
def describe_instances(self):
|
def describe_instances(self):
|
||||||
instance_ids = instance_ids_from_querystring(self.querystring)
|
instance_ids = instance_ids_from_querystring(self.querystring)
|
||||||
template = Template(EC2_DESCRIBE_INSTANCES)
|
|
||||||
if instance_ids:
|
if instance_ids:
|
||||||
reservations = ec2_backend.get_reservations_by_instance_ids(instance_ids)
|
try:
|
||||||
|
reservations = ec2_backend.get_reservations_by_instance_ids(instance_ids)
|
||||||
|
except InvalidIdError as exc:
|
||||||
|
template = Template(EC2_INVALID_INSTANCE_ID)
|
||||||
|
return template.render(instance_id=exc.instance_id), dict(status=400)
|
||||||
else:
|
else:
|
||||||
reservations = ec2_backend.all_reservations()
|
reservations = ec2_backend.all_reservations()
|
||||||
|
|
||||||
|
filter_dict = filters_from_querystring(self.querystring)
|
||||||
|
reservations = filter_reservations(reservations, filter_dict)
|
||||||
|
|
||||||
|
template = Template(EC2_DESCRIBE_INSTANCES)
|
||||||
return template.render(reservations=reservations)
|
return template.render(reservations=reservations)
|
||||||
|
|
||||||
def run_instances(self):
|
def run_instances(self):
|
||||||
@ -263,3 +272,10 @@ EC2_MODIFY_INSTANCE_ATTRIBUTE = """<ModifyInstanceAttributeResponse xmlns="http:
|
|||||||
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
|
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
|
||||||
<return>true</return>
|
<return>true</return>
|
||||||
</ModifyInstanceAttributeResponse>"""
|
</ModifyInstanceAttributeResponse>"""
|
||||||
|
|
||||||
|
|
||||||
|
EC2_INVALID_INSTANCE_ID = """<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Response><Errors><Error><Code>InvalidInstanceID.NotFound</Code>
|
||||||
|
<Message>The instance ID '{{ instance_id }}' does not exist</Message></Error>
|
||||||
|
</Errors>
|
||||||
|
<RequestID>39070fe4-6f6d-4565-aecd-7850607e4555</RequestID></Response>"""
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import random
|
import random
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
def random_id(prefix=''):
|
def random_id(prefix=''):
|
||||||
@ -53,7 +54,7 @@ def resource_ids_from_querystring(querystring_dict):
|
|||||||
prefix = 'ResourceId'
|
prefix = 'ResourceId'
|
||||||
response_values = {}
|
response_values = {}
|
||||||
for key, value in querystring_dict.iteritems():
|
for key, value in querystring_dict.iteritems():
|
||||||
if prefix in key:
|
if key.startswith(prefix):
|
||||||
resource_index = key.replace(prefix + ".", "")
|
resource_index = key.replace(prefix + ".", "")
|
||||||
tag_key = querystring_dict.get("Tag.{}.Key".format(resource_index))[0]
|
tag_key = querystring_dict.get("Tag.{}.Key".format(resource_index))[0]
|
||||||
|
|
||||||
@ -65,3 +66,42 @@ def resource_ids_from_querystring(querystring_dict):
|
|||||||
response_values[value[0]] = (tag_key, tag_value)
|
response_values[value[0]] = (tag_key, tag_value)
|
||||||
|
|
||||||
return response_values
|
return response_values
|
||||||
|
|
||||||
|
|
||||||
|
def filters_from_querystring(querystring_dict):
|
||||||
|
response_values = {}
|
||||||
|
for key, value in querystring_dict.iteritems():
|
||||||
|
match = re.search("Filter.(\d).Name", key)
|
||||||
|
if match:
|
||||||
|
filter_index = match.groups()[0]
|
||||||
|
value_prefix = "Filter.{}.Value".format(filter_index)
|
||||||
|
filter_values = [filter_value[0] for filter_key, filter_value in querystring_dict.iteritems() if filter_key.startswith(value_prefix)]
|
||||||
|
response_values[value[0]] = filter_values
|
||||||
|
return response_values
|
||||||
|
|
||||||
|
|
||||||
|
filter_dict_attribute_mapping = {
|
||||||
|
'instance-state-name': 'state'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def passes_filter_dict(instance, filter_dict):
|
||||||
|
for filter_name, filter_values in filter_dict.iteritems():
|
||||||
|
if filter_name in filter_dict_attribute_mapping:
|
||||||
|
instance_attr = filter_dict_attribute_mapping[filter_name]
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("Filter dicts have not been implemented in Moto for '%s' yet. Feel free to open an issue at https://github.com/spulec/moto/issues", filter_name)
|
||||||
|
instance_value = getattr(instance, instance_attr)
|
||||||
|
if instance_value not in filter_values:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def filter_reservations(reservations, filter_dict):
|
||||||
|
for reservation in reservations:
|
||||||
|
new_instances = []
|
||||||
|
for instance in reservation.instances:
|
||||||
|
if passes_filter_dict(instance, filter_dict):
|
||||||
|
new_instances.append(instance)
|
||||||
|
reservation.instances = new_instances
|
||||||
|
return reservations
|
||||||
|
@ -2,6 +2,7 @@ import base64
|
|||||||
|
|
||||||
import boto
|
import boto
|
||||||
from boto.ec2.instance import Reservation, InstanceAttribute
|
from boto.ec2.instance import Reservation, InstanceAttribute
|
||||||
|
from boto.exception import EC2ResponseError
|
||||||
import sure # flake8: noqa
|
import sure # flake8: noqa
|
||||||
|
|
||||||
from moto import mock_ec2
|
from moto import mock_ec2
|
||||||
@ -69,6 +70,34 @@ def test_get_instances_by_id():
|
|||||||
instance_ids = [instance.id for instance in reservation.instances]
|
instance_ids = [instance.id for instance in reservation.instances]
|
||||||
instance_ids.should.equal([instance1.id, instance2.id])
|
instance_ids.should.equal([instance1.id, instance2.id])
|
||||||
|
|
||||||
|
# Call get_all_instances with a bad id should raise an error
|
||||||
|
conn.get_all_instances.when.called_with(instance_ids=[instance1.id, "i-1234abcd"]).should.throw(
|
||||||
|
EC2ResponseError,
|
||||||
|
"The instance ID 'i-1234abcd' does not exist"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_get_instances_filtering_by_state():
|
||||||
|
conn = boto.connect_ec2()
|
||||||
|
reservation = conn.run_instances('ami-1234abcd', min_count=3)
|
||||||
|
instance1, instance2, instance3 = reservation.instances
|
||||||
|
|
||||||
|
conn.terminate_instances([instance1.id])
|
||||||
|
|
||||||
|
reservations = conn.get_all_instances(filters={'instance-state-name': 'pending'})
|
||||||
|
reservations.should.have.length_of(1)
|
||||||
|
# Since we terminated instance1, only instance2 and instance3 should be returned
|
||||||
|
instance_ids = [instance.id for instance in reservations[0].instances]
|
||||||
|
set(instance_ids).should.equal(set([instance2.id, instance3.id]))
|
||||||
|
|
||||||
|
reservations = conn.get_all_instances([instance2.id], filters={'instance-state-name': 'pending'})
|
||||||
|
reservations.should.have.length_of(1)
|
||||||
|
instance_ids = [instance.id for instance in reservations[0].instances]
|
||||||
|
instance_ids.should.equal([instance2.id])
|
||||||
|
|
||||||
|
conn.get_all_instances.when.called_with(filters={'not-implemented-filter': 'foobar'}).should.throw(NotImplementedError)
|
||||||
|
|
||||||
|
|
||||||
@mock_ec2
|
@mock_ec2
|
||||||
def test_instance_start_and_stop():
|
def test_instance_start_and_stop():
|
||||||
|
Loading…
x
Reference in New Issue
Block a user