Merge pull request #162 from andresriancho/master

Fix block device mapping #160
This commit is contained in:
Steve Pulec 2014-08-20 07:39:24 -04:00
commit 195505948b
3 changed files with 84 additions and 1 deletions

View File

@ -3,6 +3,7 @@ import itertools
from collections import defaultdict
from boto.ec2.instance import Instance as BotoInstance, Reservation
from boto.ec2.blockdevicemapping import BlockDeviceMapping, BlockDeviceType
from boto.ec2.spotinstancerequest import SpotInstanceRequest as BotoSpotRequest
from boto.ec2.launchspecification import LaunchSpecification
@ -65,6 +66,9 @@ class Instance(BotoInstance, TaggedEC2Instance):
self.subnet_id = kwargs.get("subnet_id")
self.key_name = kwargs.get("key_name")
self.block_device_mapping = BlockDeviceMapping()
self.block_device_mapping['/dev/sda1'] = BlockDeviceType()
@classmethod
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
properties = cloudformation_json['Properties']

View File

@ -66,7 +66,8 @@ class InstanceResponse(BaseResponse):
return template.render(instances=instances)
def describe_instance_attribute(self):
# TODO this and modify below should raise IncorrectInstanceState if instance not in stopped state
# TODO this and modify below should raise IncorrectInstanceState if
# instance not in stopped state
attribute = self.querystring.get("Attribute")[0]
key = camelcase_to_underscores(attribute)
instance_ids = instance_ids_from_querystring(self.querystring)
@ -76,6 +77,61 @@ class InstanceResponse(BaseResponse):
return template.render(instance=instance, attribute=attribute, value=value)
def modify_instance_attribute(self):
handlers = [self._dot_value_instance_attribute_handler,
self._block_device_mapping_handler]
for handler in handlers:
success = handler()
if success:
return success
msg = "This specific call to ModifyInstanceAttribute has not been" \
" implemented in Moto yet. Feel free to open an issue at" \
" https://github.com/spulec/moto/issues"
raise NotImplementedError(msg)
def _block_device_mapping_handler(self):
"""
Handles requests which are generated by code similar to:
instance.modify_attribute('blockDeviceMapping', {'/dev/sda1': True})
The querystring contains information similar to:
BlockDeviceMapping.1.Ebs.DeleteOnTermination : ['true']
BlockDeviceMapping.1.DeviceName : ['/dev/sda1']
For now we only support the "BlockDeviceMapping.1.Ebs.DeleteOnTermination"
configuration, but it should be trivial to add anything else.
"""
mapping_counter = 1
mapping_device_name_fmt = 'BlockDeviceMapping.%s.DeviceName'
mapping_del_on_term_fmt = 'BlockDeviceMapping.%s.Ebs.DeleteOnTermination'
while True:
mapping_device_name = mapping_device_name_fmt % mapping_counter
if mapping_device_name not in self.querystring.keys():
break
mapping_del_on_term = mapping_del_on_term_fmt % mapping_counter
del_on_term_value_str = self.querystring[mapping_del_on_term][0]
del_on_term_value = True if 'true' == del_on_term_value_str else False
device_name_value = self.querystring[mapping_device_name][0]
instance_ids = instance_ids_from_querystring(self.querystring)
instance_id = instance_ids[0]
instance = ec2_backend.get_instance(instance_id)
block_device_type = instance.block_device_mapping[device_name_value]
block_device_type.delete_on_termination = del_on_term_value
# +1 for the next device
mapping_counter += 1
if mapping_counter > 1:
return EC2_MODIFY_INSTANCE_ATTRIBUTE
def _dot_value_instance_attribute_handler(self):
attribute_key = None
for key, value in self.querystring.iteritems():
if '.Value' in key:
@ -84,6 +140,7 @@ class InstanceResponse(BaseResponse):
if not attribute_key:
return
value = self.querystring.get(attribute_key)[0]
normalized_attribute = camelcase_to_underscores(attribute_key.split(".")[0])
instance_ids = instance_ids_from_querystring(self.querystring)

View File

@ -3,6 +3,7 @@ from boto.exception import EC2ResponseError
import sure # noqa
from moto import mock_ec2
from moto.ec2.models import ec2_backend
@mock_ec2
@ -76,3 +77,24 @@ def test_create_snapshot():
# Deleting something that was already deleted should throw an error
snapshot.delete.when.called_with().should.throw(EC2ResponseError)
@mock_ec2
def test_modify_attribute_blockDeviceMapping():
"""
Reproduces the missing feature explained at [0], where we want to mock a
call to modify an instance attribute of type: blockDeviceMapping.
[0] https://github.com/spulec/moto/issues/160
"""
conn = boto.connect_ec2('the_key', 'the_secret')
reservation = conn.run_instances('ami-1234abcd')
instance = reservation.instances[0]
instance.modify_attribute('blockDeviceMapping', {'/dev/sda1': True})
instance = ec2_backend.get_instance(instance.id)
instance.block_device_mapping.should.have.key('/dev/sda1')
instance.block_device_mapping['/dev/sda1'].delete_on_termination.should.be(True)