diff --git a/moto/autoscaling/models.py b/moto/autoscaling/models.py index a80337099..d4d5c794d 100644 --- a/moto/autoscaling/models.py +++ b/moto/autoscaling/models.py @@ -1,3 +1,4 @@ +from boto.ec2.blockdevicemapping import BlockDeviceType, BlockDeviceMapping from moto.core import BaseBackend from moto.ec2 import ec2_backend @@ -29,7 +30,7 @@ class FakeScalingPolicy(object): class FakeLaunchConfiguration(object): def __init__(self, name, image_id, key_name, security_groups, user_data, instance_type, instance_monitoring, instance_profile_name, - spot_price, ebs_optimized, associate_public_ip_address): + spot_price, ebs_optimized, associate_public_ip_address, block_device_mapping_dict): self.name = name self.image_id = image_id self.key_name = key_name @@ -40,6 +41,7 @@ class FakeLaunchConfiguration(object): self.instance_profile_name = instance_profile_name self.spot_price = spot_price self.ebs_optimized = ebs_optimized +<<<<<<< HEAD self.associate_public_ip_address = associate_public_ip_address @classmethod @@ -66,6 +68,16 @@ class FakeLaunchConfiguration(object): @property def physical_resource_id(self): return self.name +======= + self.block_device_mapping_dict = block_device_mapping_dict + + @property + def block_device_mappings(self): + if not self.block_device_mapping_dict: + return None + else: + return self._parse_block_device_mappings() +>>>>>>> [Block Device] Add block device mapping to launch config backend @property def instance_monitoring_enabled(self): @@ -73,6 +85,22 @@ class FakeLaunchConfiguration(object): return 'true' return 'false' + def _parse_block_device_mappings(self): + block_device_map = BlockDeviceMapping() + for mapping in self.block_device_mapping_dict: + block_type = BlockDeviceType() + mount_point = mapping.get('device_name') + if 'ephemeral' in mapping.get('virtual_name', ''): + block_type.ephemeral_name = mapping.get('virtual_name') + else: + block_type.volume_type = mapping.get('ebs._volume_type') + block_type.snapshot_id = mapping.get('ebs._snapshot_id') + block_type.delete_on_termination = mapping.get('ebs._delete_on_termination') + block_type.size = mapping.get('ebs._volume_size') + block_type.iops = mapping.get('ebs._iops') + block_device_map[mount_point] = block_type + return block_device_map + class FakeAutoScalingGroup(object): def __init__(self, name, availability_zones, desired_capacity, max_size, @@ -184,7 +212,11 @@ class AutoScalingBackend(BaseBackend): def create_launch_configuration(self, name, image_id, key_name, security_groups, user_data, instance_type, instance_monitoring, instance_profile_name, +<<<<<<< HEAD spot_price, ebs_optimized, associate_public_ip_address): +======= + spot_price, ebs_optimized, block_device_mappings): +>>>>>>> [Block Device] Add block device mapping to launch config backend launch_configuration = FakeLaunchConfiguration( name=name, image_id=image_id, @@ -196,7 +228,11 @@ class AutoScalingBackend(BaseBackend): instance_profile_name=instance_profile_name, spot_price=spot_price, ebs_optimized=ebs_optimized, +<<<<<<< HEAD associate_public_ip_address=associate_public_ip_address, +======= + block_device_mapping_dict=block_device_mappings, +>>>>>>> [Block Device] Add block device mapping to launch config backend ) self.launch_configurations[name] = launch_configuration return launch_configuration diff --git a/moto/autoscaling/responses.py b/moto/autoscaling/responses.py index a881919e7..32eb7b301 100644 --- a/moto/autoscaling/responses.py +++ b/moto/autoscaling/responses.py @@ -1,6 +1,7 @@ from jinja2 import Template from moto.core.responses import BaseResponse +from moto.core.utils import camelcase_to_underscores from .models import autoscaling_backend @@ -17,6 +18,21 @@ class AutoScalingResponse(BaseResponse): def _get_multi_param(self, param_prefix): return [value[0] for key, value in self.querystring.items() if key.startswith(param_prefix)] + def _get_list_prefix(self, param_prefix): + results = [] + param_index = 1 + while True: + index_prefix = "{0}.{1}.".format(param_prefix, param_index) + new_items = {} + for key, value in self.querystring.items(): + if key.startswith(index_prefix): + new_items[camelcase_to_underscores(key.replace(index_prefix, ""))] = value[0] + if not new_items: + break + results.append(new_items) + param_index += 1 + return results + def create_launch_configuration(self): instance_monitoring_string = self._get_param('InstanceMonitoring.Enabled') if instance_monitoring_string == 'true': @@ -35,6 +51,7 @@ class AutoScalingResponse(BaseResponse): spot_price=self._get_param('SpotPrice'), ebs_optimized=self._get_param('EbsOptimized'), associate_public_ip_address=self._get_param("AssociatePublicIpAddress"), + block_device_mappings=self._get_list_prefix('BlockDeviceMappings.member') ) template = Template(CREATE_LAUNCH_CONFIGURATION_TEMPLATE) return template.render() @@ -173,7 +190,34 @@ DESCRIBE_LAUNCH_CONFIGURATIONS_TEMPLATE = """m1.small arn:aws:autoscaling:us-east-1:803981987763:launchConfiguration: 9dbbbf87-6141-428a-a409-0752edbe6cad:launchConfigurationName/my-test-lc - + {% if launch_configuration.block_device_mappings %} + + {% for mount_point, mapping in launch_configuration.block_device_mappings.iteritems() %} + + {{ mount_point }} + {% if mapping.ephemeral_name %} + {{ mapping.ephemeral_name }} + {% else %} + + {% if mapping.snapshot_id %} + {{ mapping.snapshot_id }} + {% endif %} + {% if mapping.size %} + {{ mapping.size }} + {% endif %} + {% if mapping.iops %} + {{ mapping.iops }} + {% endif %} + {{ mapping.delete_on_termination }} + {{ mapping.volume_type }} + + {% endif %} + + {% endfor %} + + {% else %} + + {% endif %} {{ launch_configuration.image_id }} {% if launch_configuration.key_name %} {{ launch_configuration.key_name }} diff --git a/tests/test_autoscaling/test_launch_configurations.py b/tests/test_autoscaling/test_launch_configurations.py index fcbc2f6da..10f7dbc42 100644 --- a/tests/test_autoscaling/test_launch_configurations.py +++ b/tests/test_autoscaling/test_launch_configurations.py @@ -1,5 +1,6 @@ import boto from boto.ec2.autoscale.launchconfig import LaunchConfiguration +from boto.ec2.blockdevicemapping import BlockDeviceType, BlockDeviceMapping import sure # noqa @@ -35,6 +36,72 @@ def test_create_launch_configuration(): launch_config.spot_price.should.equal(0.1) +@mock_autoscaling +def test_create_launch_configuration_with_block_device_mappings(): + block_device_mapping = BlockDeviceMapping() + + ephemeral_drive = BlockDeviceType() + ephemeral_drive.ephemeral_name = 'ephemeral0' + block_device_mapping['/dev/xvdb'] = ephemeral_drive + + snapshot_drive = BlockDeviceType() + snapshot_drive.snapshot_id = "snap-1234abcd" + snapshot_drive.volume_type = "standard" + block_device_mapping['/dev/xvdp'] = snapshot_drive + + ebs_drive = BlockDeviceType() + ebs_drive.volume_type = "io1" + ebs_drive.size = 100 + ebs_drive.iops = 1000 + ebs_drive.delete_on_termination = False + block_device_mapping['/dev/xvdh'] = ebs_drive + + conn = boto.connect_autoscale() + config = LaunchConfiguration( + name='tester', + image_id='ami-abcd1234', + instance_type='m1.small', + key_name='the_keys', + security_groups=["default", "default2"], + user_data="This is some user_data", + instance_monitoring=True, + instance_profile_name='arn:aws:iam::123456789012:instance-profile/testing', + spot_price=0.1, + block_device_mappings=[block_device_mapping] + ) + conn.create_launch_configuration(config) + + launch_config = conn.get_all_launch_configurations()[0] + launch_config.name.should.equal('tester') + launch_config.image_id.should.equal('ami-abcd1234') + launch_config.instance_type.should.equal('m1.small') + launch_config.key_name.should.equal('the_keys') + set(launch_config.security_groups).should.equal(set(['default', 'default2'])) + launch_config.user_data.should.equal("This is some user_data") + launch_config.instance_monitoring.enabled.should.equal('true') + launch_config.instance_profile_name.should.equal('arn:aws:iam::123456789012:instance-profile/testing') + launch_config.spot_price.should.equal(0.1) + len(launch_config.block_device_mappings).should.equal(3) + + # Unsure why boto returns results in a different format than it takes them + # returned_mapping = BlockDeviceMapping() + # for mapping in launch_config.block_device_mappings: + # returned_mapping[mapping.device_name] = mapping + + # Broken due to Boto bug + # set(returned_mapping.keys()).should.equal(set(['/dev/xvdb', '/dev/xvdp', '/dev/xvdh'])) + + # returned_mapping['/dev/xvdh'].ebs.iops.should.equal(1000) + # returned_mapping['/dev/xvdh'].ebs.volume_size.should.equal(100) + # returned_mapping['/dev/xvdh'].ebs.volume_type.shoud.equal("io1") + # returned_mapping['/dev/xvdh'].ebs.delete_on_termination.shoud.be.false + + # returned_mapping['/dev/xvdp'].ebs.snapshot_id.should.equal("snap-1234abcd") + # returned_mapping['/dev/xvdp'].ebs.volume_type.shoud.equal("standard") + + # returned_mapping['/dev/xvdb'].ephemeral_name.should.equal('ephemeral0') + + @requires_boto_gte("2.12") @mock_autoscaling def test_create_launch_configuration_for_2_12():