Merge pull request #234 from DataDog/add_instance_reason
EC2: Add instance state reason
This commit is contained in:
commit
bcef13700d
@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
|||||||
import copy
|
import copy
|
||||||
import itertools
|
import itertools
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
import six
|
import six
|
||||||
import boto
|
import boto
|
||||||
@ -96,6 +97,11 @@ class InstanceState(object):
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.code = code
|
self.code = code
|
||||||
|
|
||||||
|
class StateReason(object):
|
||||||
|
def __init__(self, message="", code=""):
|
||||||
|
self.message = message
|
||||||
|
self.code = code
|
||||||
|
|
||||||
|
|
||||||
class TaggedEC2Resource(object):
|
class TaggedEC2Resource(object):
|
||||||
def get_tags(self, *args, **kwargs):
|
def get_tags(self, *args, **kwargs):
|
||||||
@ -259,6 +265,8 @@ class Instance(BotoInstance, TaggedEC2Resource):
|
|||||||
self.id = random_instance_id()
|
self.id = random_instance_id()
|
||||||
self.image_id = image_id
|
self.image_id = image_id
|
||||||
self._state = InstanceState("running", 16)
|
self._state = InstanceState("running", 16)
|
||||||
|
self._reason = ""
|
||||||
|
self._state_reason = StateReason()
|
||||||
self.user_data = user_data
|
self.user_data = user_data
|
||||||
self.security_groups = security_groups
|
self.security_groups = security_groups
|
||||||
self.instance_type = kwargs.get("instance_type", "m1.small")
|
self.instance_type = kwargs.get("instance_type", "m1.small")
|
||||||
@ -318,6 +326,9 @@ class Instance(BotoInstance, TaggedEC2Resource):
|
|||||||
self._state.name = "running"
|
self._state.name = "running"
|
||||||
self._state.code = 16
|
self._state.code = 16
|
||||||
|
|
||||||
|
self._reason = ""
|
||||||
|
self._state_reason = StateReason()
|
||||||
|
|
||||||
def stop(self, *args, **kwargs):
|
def stop(self, *args, **kwargs):
|
||||||
for nic in self.nics.values():
|
for nic in self.nics.values():
|
||||||
nic.stop()
|
nic.stop()
|
||||||
@ -325,6 +336,10 @@ class Instance(BotoInstance, TaggedEC2Resource):
|
|||||||
self._state.name = "stopped"
|
self._state.name = "stopped"
|
||||||
self._state.code = 80
|
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):
|
def terminate(self, *args, **kwargs):
|
||||||
for nic in self.nics.values():
|
for nic in self.nics.values():
|
||||||
nic.stop()
|
nic.stop()
|
||||||
@ -332,10 +347,17 @@ class Instance(BotoInstance, TaggedEC2Resource):
|
|||||||
self._state.name = "terminated"
|
self._state.name = "terminated"
|
||||||
self._state.code = 48
|
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):
|
def reboot(self, *args, **kwargs):
|
||||||
self._state.name = "running"
|
self._state.name = "running"
|
||||||
self._state.code = 16
|
self._state.code = 16
|
||||||
|
|
||||||
|
self._reason = ""
|
||||||
|
self._state_reason = StateReason()
|
||||||
|
|
||||||
def get_tags(self):
|
def get_tags(self):
|
||||||
tags = ec2_backend.describe_tags(filters={'resource-id': [self.id]})
|
tags = ec2_backend.describe_tags(filters={'resource-id': [self.id]})
|
||||||
return tags
|
return tags
|
||||||
|
@ -303,7 +303,7 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns='http://ec2.amazona
|
|||||||
</instanceState>
|
</instanceState>
|
||||||
<privateDnsName>ip-10.0.0.12.ec2.internal</privateDnsName>
|
<privateDnsName>ip-10.0.0.12.ec2.internal</privateDnsName>
|
||||||
<dnsName>ec2-46.51.219.63.compute-1.amazonaws.com</dnsName>
|
<dnsName>ec2-46.51.219.63.compute-1.amazonaws.com</dnsName>
|
||||||
<reason/>
|
<reason>{{ instance._reason }}</reason>
|
||||||
<keyName>{{ instance.key_name }}</keyName>
|
<keyName>{{ instance.key_name }}</keyName>
|
||||||
<amiLaunchIndex>0</amiLaunchIndex>
|
<amiLaunchIndex>0</amiLaunchIndex>
|
||||||
<productCodes/>
|
<productCodes/>
|
||||||
@ -337,6 +337,10 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns='http://ec2.amazona
|
|||||||
</item>
|
</item>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</groupSet>
|
</groupSet>
|
||||||
|
<stateReason>
|
||||||
|
<code>{{ instance._state_reason.code }}</code>
|
||||||
|
<message>{{ instance._state_reason.message }}</message>
|
||||||
|
</stateReason>
|
||||||
<architecture>{{ instance.architecture }}</architecture>
|
<architecture>{{ instance.architecture }}</architecture>
|
||||||
<kernelId>{{ instance.kernel }}</kernelId>
|
<kernelId>{{ instance.kernel }}</kernelId>
|
||||||
<rootDeviceType>ebs</rootDeviceType>
|
<rootDeviceType>ebs</rootDeviceType>
|
||||||
|
@ -271,15 +271,27 @@ def keypair_names_from_querystring(querystring_dict):
|
|||||||
|
|
||||||
filter_dict_attribute_mapping = {
|
filter_dict_attribute_mapping = {
|
||||||
'instance-state-name': 'state',
|
'instance-state-name': 'state',
|
||||||
'instance-id': 'id'
|
'instance-id': 'id',
|
||||||
|
'state-reason-code': '_state_reason.code',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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):
|
def passes_filter_dict(instance, filter_dict):
|
||||||
for filter_name, filter_values in filter_dict.items():
|
for filter_name, filter_values in filter_dict.items():
|
||||||
if filter_name in filter_dict_attribute_mapping:
|
if filter_name in filter_dict_attribute_mapping:
|
||||||
instance_attr = filter_dict_attribute_mapping[filter_name]
|
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:
|
if instance_value not in filter_values:
|
||||||
return False
|
return False
|
||||||
elif filter_name.startswith('tag:'):
|
elif filter_name.startswith('tag:'):
|
||||||
|
@ -135,6 +135,24 @@ def test_get_instances_filtering_by_instance_id():
|
|||||||
reservations = conn.get_all_instances(filters={'instance-id': 'non-existing-id'})
|
reservations = conn.get_all_instances(filters={'instance-id': 'non-existing-id'})
|
||||||
reservations.should.have.length_of(0)
|
reservations.should.have.length_of(0)
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_get_instances_filtering_by_reason_code():
|
||||||
|
conn = boto.connect_ec2()
|
||||||
|
reservation = conn.run_instances('ami-1234abcd', min_count=3)
|
||||||
|
instance1, instance2, instance3 = reservation.instances
|
||||||
|
instance1.stop()
|
||||||
|
instance2.terminate()
|
||||||
|
|
||||||
|
reservations = conn.get_all_instances(filters={'state-reason-code': 'Client.UserInitiatedShutdown'})
|
||||||
|
# get_all_instances should return instance1 and instance2
|
||||||
|
reservations[0].instances.should.have.length_of(2)
|
||||||
|
set([instance1.id, instance2.id]).should.equal(set([i.id for i in reservations[0].instances]))
|
||||||
|
|
||||||
|
reservations = conn.get_all_instances(filters={'state-reason-code': ''})
|
||||||
|
# get_all_instances should return instance 3
|
||||||
|
reservations[0].instances.should.have.length_of(1)
|
||||||
|
reservations[0].instances[0].id.should.equal(instance3.id)
|
||||||
|
|
||||||
@mock_ec2
|
@mock_ec2
|
||||||
def test_get_instances_filtering_by_tag():
|
def test_get_instances_filtering_by_tag():
|
||||||
conn = boto.connect_ec2()
|
conn = boto.connect_ec2()
|
||||||
|
Loading…
Reference in New Issue
Block a user