From 6963866c7eece36ee9ce049ee7548994b1399971 Mon Sep 17 00:00:00 2001 From: Arthur Wang Date: Mon, 20 Oct 2014 15:54:00 -0400 Subject: [PATCH] Add ec2 instance state reason - Add instance.reason and instance.state_reason (http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-Item Type-StateReasonType.html) - Add ec2 filtering by state-reason-code and state-reason-message --- moto/ec2/models.py | 22 ++++++++++++++++++++++ moto/ec2/responses/instances.py | 6 +++++- moto/ec2/utils.py | 17 +++++++++++++++-- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/moto/ec2/models.py b/moto/ec2/models.py index 32b6b166..0540abc1 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals import copy import itertools from collections import defaultdict +from datetime import datetime import six import boto @@ -95,6 +96,11 @@ class InstanceState(object): self.name = name self.code = code +class StateReason(object): + def __init__(self, message="", code=""): + self.message = message + self.code = code + class TaggedEC2Resource(object): def get_tags(self, *args, **kwargs): @@ -258,6 +264,8 @@ class Instance(BotoInstance, TaggedEC2Resource): self.id = random_instance_id() self.image_id = image_id self._state = InstanceState("running", 16) + self._reason = "" + self._state_reason = StateReason() self.user_data = user_data self.security_groups = security_groups self.instance_type = kwargs.get("instance_type", "m1.small") @@ -317,6 +325,9 @@ class Instance(BotoInstance, TaggedEC2Resource): self._state.name = "running" self._state.code = 16 + self._reason = "" + self._state_reason = StateReason() + def stop(self, *args, **kwargs): for nic in self.nics.values(): nic.stop() @@ -324,6 +335,10 @@ class Instance(BotoInstance, TaggedEC2Resource): self._state.name = "stopped" self._state.code = 80 + self._reason = "User initiated ({0})".format(datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S UTC')) + self._state_reason = StateReason("Client.UserInitiatedShutdown: User initiated shutdown", + "Client.UserInitiatedShutdown") + def terminate(self, *args, **kwargs): for nic in self.nics.values(): nic.stop() @@ -331,10 +346,17 @@ class Instance(BotoInstance, TaggedEC2Resource): self._state.name = "terminated" self._state.code = 48 + self._reason = "User initiated ({0})".format(datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S UTC')) + self._state_reason = StateReason("Client.UserInitiatedShutdown: User initiated shutdown", + "Client.UserInitiatedShutdown") + def reboot(self, *args, **kwargs): self._state.name = "running" self._state.code = 16 + self._reason = "" + self._state_reason = StateReason() + def get_tags(self): tags = ec2_backend.describe_tags(filters={'resource-id': [self.id]}) return tags diff --git a/moto/ec2/responses/instances.py b/moto/ec2/responses/instances.py index b1833ee2..70f19a8f 100644 --- a/moto/ec2/responses/instances.py +++ b/moto/ec2/responses/instances.py @@ -303,7 +303,7 @@ EC2_DESCRIBE_INSTANCES = """ {% endfor %} + + {{ instance._state_reason.code }} + {{ instance._state_reason.message }} + {{ instance.architecture }} {{ instance.kernel }} ebs diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py index 1a590d6d..06ff1941 100644 --- a/moto/ec2/utils.py +++ b/moto/ec2/utils.py @@ -266,15 +266,28 @@ def keypair_names_from_querystring(querystring_dict): filter_dict_attribute_mapping = { 'instance-state-name': 'state', - 'instance-id': 'id' + 'instance-id': 'id', + 'state-reason-code': '_state_reason.code', + 'state-reason-message': '_state_reason.message' } +def get_instance_value(instance, instance_attr): + keys = instance_attr.split('.') + val = instance + for key in keys: + if hasattr(val, key): + val = getattr(val, key) + elif isinstance(val, dict): + val = val[key] + else: + return None + return val def passes_filter_dict(instance, filter_dict): for filter_name, filter_values in filter_dict.items(): if filter_name in filter_dict_attribute_mapping: instance_attr = filter_dict_attribute_mapping[filter_name] - instance_value = getattr(instance, instance_attr) + instance_value = get_instance_value(instance, instance_attr) if instance_value not in filter_values: return False elif filter_name.startswith('tag:'):