Added the ability to add volumes to instances at creation time

This allows a volume to be cloned from a snapshot, a brand new
volume to be created, or an ephemeral volumes to be associated
at time of instance creation.

This avoids any race conditions associated with creating extra volumes
after instance creation (e.g. writes happening before the volume is
mounted).

In addition, this allows the root volume to be edited
This commit is contained in:
willthames 2013-10-16 15:37:24 +10:00 committed by Will Thames
parent 67fba9c38c
commit 205dddfd4c

View file

@ -191,6 +191,13 @@ options:
required: false required: false
default: 'present' default: 'present'
aliases: [] aliases: []
volumes:
version_added: "1.5"
description:
- a list of volume dicts, each containing device name and optionally ephemeral id or snapshot id. Size and type (and number of iops for io device type) must be specified for a new volume or a root volume, and may be passed for a snapshot volume. For any volume, a volume size less than 1 will be interpreted as a request not to create the volume.
required: false
default: null
aliases: []
requirements: [ "boto" ] requirements: [ "boto" ]
author: Seth Vidal, Tim Gerla, Lester Wade author: Seth Vidal, Tim Gerla, Lester Wade
@ -223,6 +230,23 @@ EXAMPLES = '''
instance_tags: '{"db":"postgres"}' instance_tags: '{"db":"postgres"}'
monitoring=yes monitoring=yes
# Single instance with additional IOPS volume from snapshot
local_action:
module: ec2
keypair: mykey
group: webserver
instance_type: m1.large
image: ami-6e649707
wait: yes
wait_timeout: 500
volumes:
- device_name: /dev/sdb
snapshot: snap-abcdef12
device_type: io1
iops: 1000
volume_size: 100
monitoring=yes
# Multiple groups example # Multiple groups example
local_action: local_action:
module: ec2 module: ec2
@ -236,6 +260,22 @@ local_action:
instance_tags: '{"db":"postgres"}' instance_tags: '{"db":"postgres"}'
monitoring=yes monitoring=yes
# Multiple instances with additional volume from snapshot
local_action:
module: ec2
keypair: mykey
group: webserver
instance_type: m1.large
image: ami-6e649707
wait: yes
wait_timeout: 500
count: 5
volumes:
- device_name: /dev/sdb
snapshot: snap-abcdef12
volume_size: 10
monitoring=yes
# VPC example # VPC example
- local_action: - local_action:
module: ec2 module: ec2
@ -296,6 +336,7 @@ import time
try: try:
import boto.ec2 import boto.ec2
from boto.ec2.blockdevicemapping import BlockDeviceType, BlockDeviceMapping
from boto.exception import EC2ResponseError from boto.exception import EC2ResponseError
except ImportError: except ImportError:
print "failed=True msg='boto required for this module'" print "failed=True msg='boto required for this module'"
@ -364,6 +405,30 @@ def boto_supports_profile_name_arg(ec2):
run_instances_method = getattr(ec2, 'run_instances') run_instances_method = getattr(ec2, 'run_instances')
return 'instance_profile_name' in run_instances_method.func_code.co_varnames return 'instance_profile_name' in run_instances_method.func_code.co_varnames
def create_block_device(module, ec2, volume):
# Not aware of a way to determine this programatically
# http://aws.amazon.com/about-aws/whats-new/2013/10/09/ebs-provisioned-iops-maximum-iops-gb-ratio-increased-to-30-1/
MAX_IOPS_TO_SIZE_RATIO = 30
if 'snapshot' not in volume and 'ephemeral' not in volume:
if 'volume_size' not in volume:
module.fail_json(msg = 'Size must be specified when creating a new volume or modifying the root volume')
if 'snapshot' in volume:
if 'device_type' in volume and volume.get('device_type') == 'io1' and 'iops' not in volume:
module.fail_json(msg = 'io1 volumes must have an iops value set')
if 'iops' in volume:
snapshot = ec2.get_all_snapshots(snapshot_ids=[volume['snapshot']])[0]
size = volume.get('volume_size', snapshot.volume_size)
if int(volume['iops']) > MAX_IOPS_TO_SIZE_RATIO * size:
module.fail_json(msg = 'IOPS must be at most %d times greater than size' % MAX_IOPS_TO_SIZE_RATIO)
if 'ephemeral' in volume:
if 'snapshot' in volume:
module.fail_json(msg = 'Cannot set both ephemeral and snapshot')
return BlockDeviceType(snapshot_id=volume.get('snapshot'),
ephemeral_name=volume.get('ephemeral'),
size=volume.get('volume_size'),
volume_type=volume.get('device_type'),
delete_on_termination=volume.get('delete_on_termination', False),
iops=volume.get('iops'))
def create_instances(module, ec2): def create_instances(module, ec2):
""" """
@ -397,6 +462,7 @@ def create_instances(module, ec2):
assign_public_ip = module.boolean(module.params.get('assign_public_ip')) assign_public_ip = module.boolean(module.params.get('assign_public_ip'))
private_ip = module.params.get('private_ip') private_ip = module.params.get('private_ip')
instance_profile_name = module.params.get('instance_profile_name') instance_profile_name = module.params.get('instance_profile_name')
volumes = module.params.get('volumes')
# group_id and group_name are exclusive of each other # group_id and group_name are exclusive of each other
if group_id and group_name: if group_id and group_name:
@ -489,6 +555,18 @@ def create_instances(module, ec2):
else: else:
params['security_groups'] = group_name params['security_groups'] = group_name
if volumes:
bdm = BlockDeviceMapping()
for volume in volumes:
if 'device_name' not in volume:
module.fail_json(msg = 'Device name must be set for volume')
# Minimum volume size is 1GB. We'll use volume size explicitly set to 0
# to be a signal not to create this volume
if 'volume_size' not in volume or int(volume['volume_size']) > 0:
bdm[volume['device_name']] = create_block_device(module, ec2, volume)
params['block_device_map'] = bdm
res = ec2.run_instances(**params) res = ec2.run_instances(**params)
except boto.exception.BotoServerError, e: except boto.exception.BotoServerError, e:
module.fail_json(msg = "%s: %s" % (e.error_code, e.error_message)) module.fail_json(msg = "%s: %s" % (e.error_code, e.error_message))
@ -641,6 +719,7 @@ def main():
instance_profile_name = dict(), instance_profile_name = dict(),
instance_ids = dict(type='list'), instance_ids = dict(type='list'),
state = dict(default='present'), state = dict(default='present'),
volumes = dict(type='list'),
) )
) )