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:
parent
67fba9c38c
commit
205dddfd4c
1 changed files with 79 additions and 0 deletions
79
cloud/ec2
79
cloud/ec2
|
@ -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'),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue