|
|
|
@ -17,9 +17,9 @@
|
|
|
|
|
DOCUMENTATION = '''
|
|
|
|
|
---
|
|
|
|
|
module: ec2
|
|
|
|
|
short_description: create an instance in ec2, return instanceid
|
|
|
|
|
short_description: create or terminate an instance in ec2, return instanceid
|
|
|
|
|
description:
|
|
|
|
|
- creates ec2 instances and optionally waits for it to be 'running'. This module has a dependency on python-boto >= 2.5
|
|
|
|
|
- Creates or terminates ec2 instances. When created optionally waits for it to be 'running'. This module has a dependency on python-boto >= 2.5
|
|
|
|
|
version_added: "0.9"
|
|
|
|
|
options:
|
|
|
|
|
key_name:
|
|
|
|
@ -163,6 +163,22 @@ options:
|
|
|
|
|
required: false
|
|
|
|
|
defualt: null
|
|
|
|
|
aliases: []
|
|
|
|
|
instance_ids:
|
|
|
|
|
version_added: "1.3"
|
|
|
|
|
description:
|
|
|
|
|
- list of instance ids, currently only used when state='absent'
|
|
|
|
|
required: false
|
|
|
|
|
default: null
|
|
|
|
|
aliases: []
|
|
|
|
|
state:
|
|
|
|
|
version_added: "1.3"
|
|
|
|
|
description:
|
|
|
|
|
- create or terminate instances
|
|
|
|
|
required: false
|
|
|
|
|
default: 'present'
|
|
|
|
|
aliases: []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements: [ "boto" ]
|
|
|
|
|
author: Seth Vidal, Tim Gerla, Lester Wade
|
|
|
|
|
'''
|
|
|
|
@ -211,6 +227,50 @@ local_action:
|
|
|
|
|
image: ami-6e649707
|
|
|
|
|
wait: yes
|
|
|
|
|
vpc_subnet_id: subnet-29e63245'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Launch instances, runs some tasks
|
|
|
|
|
# and then terminate them
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- name: Create a sandbox instance
|
|
|
|
|
hosts: localhost
|
|
|
|
|
gather_facts: False
|
|
|
|
|
vars:
|
|
|
|
|
keypair: my_keypair
|
|
|
|
|
instance_type: m1.small
|
|
|
|
|
security_group: my_securitygroup
|
|
|
|
|
image: my_ami_id
|
|
|
|
|
region: us-east-1
|
|
|
|
|
tasks:
|
|
|
|
|
- name: Launch instance
|
|
|
|
|
local_action: ec2 keypair=$keypair group=$security_group instance_type=$instance_type image=$image wait=true region=$region
|
|
|
|
|
register: ec2
|
|
|
|
|
- name: Add new instance to host group
|
|
|
|
|
local_action: add_host hostname=${item.public_ip} groupname=launched
|
|
|
|
|
with_items: ${ec2.instances}
|
|
|
|
|
- name: Wait for SSH to come up
|
|
|
|
|
local_action: wait_for host=${item.public_dns_name} port=22 delay=60 timeout=320 state=started
|
|
|
|
|
with_items: ${ec2.instances}
|
|
|
|
|
|
|
|
|
|
- name: Configure instance(s)
|
|
|
|
|
hosts: launched
|
|
|
|
|
sudo: True
|
|
|
|
|
gather_facts: True
|
|
|
|
|
roles:
|
|
|
|
|
- my_awesome_role
|
|
|
|
|
- my_awesome_test
|
|
|
|
|
|
|
|
|
|
- name: Terminate instances
|
|
|
|
|
hosts: localhost
|
|
|
|
|
connection: local
|
|
|
|
|
tasks:
|
|
|
|
|
- name: Terminate instances that were previously launched
|
|
|
|
|
local_action:
|
|
|
|
|
module: ec2
|
|
|
|
|
state: 'absent'
|
|
|
|
|
instance_ids: {{ec2.instance_ids}}
|
|
|
|
|
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
import sys
|
|
|
|
@ -218,43 +278,59 @@ import time
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
import boto.ec2
|
|
|
|
|
from boto.exception import EC2ResponseError
|
|
|
|
|
except ImportError:
|
|
|
|
|
print "failed=True msg='boto required for this module'"
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
module = AnsibleModule(
|
|
|
|
|
argument_spec = dict(
|
|
|
|
|
key_name = dict(required=True, aliases = ['keypair']),
|
|
|
|
|
id = dict(),
|
|
|
|
|
group = dict(type='list'),
|
|
|
|
|
group_id = dict(),
|
|
|
|
|
region = dict(choices=['eu-west-1', 'sa-east-1', 'us-east-1', 'ap-northeast-1', 'us-west-2', 'us-west-1', 'ap-southeast-1', 'ap-southeast-2']),
|
|
|
|
|
zone = dict(),
|
|
|
|
|
instance_type = dict(aliases=['type']),
|
|
|
|
|
image = dict(required=True),
|
|
|
|
|
kernel = dict(),
|
|
|
|
|
count = dict(default='1'),
|
|
|
|
|
monitoring = dict(choices=BOOLEANS, default=False),
|
|
|
|
|
ramdisk = dict(),
|
|
|
|
|
wait = dict(choices=BOOLEANS, default=False),
|
|
|
|
|
wait_timeout = dict(default=300),
|
|
|
|
|
ec2_url = dict(aliases=['EC2_URL']),
|
|
|
|
|
ec2_secret_key = dict(aliases=['EC2_SECRET_KEY'], no_log=True),
|
|
|
|
|
ec2_access_key = dict(aliases=['EC2_ACCESS_KEY']),
|
|
|
|
|
placement_group = dict(),
|
|
|
|
|
user_data = dict(),
|
|
|
|
|
instance_tags = dict(),
|
|
|
|
|
vpc_subnet_id = dict(),
|
|
|
|
|
private_ip = dict(),
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def get_instance_info(inst):
|
|
|
|
|
"""
|
|
|
|
|
Retrieves instance information from an instance
|
|
|
|
|
ID and returns it as a dictionary
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
return({
|
|
|
|
|
'id': inst.id,
|
|
|
|
|
'ami_launch_index': inst.ami_launch_index,
|
|
|
|
|
'private_ip': inst.private_ip_address,
|
|
|
|
|
'private_dns_name': inst.private_dns_name,
|
|
|
|
|
'public_ip': inst.ip_address,
|
|
|
|
|
'dns_name': inst.dns_name,
|
|
|
|
|
'public_dns_name': inst.public_dns_name,
|
|
|
|
|
'state_code': inst.state_code,
|
|
|
|
|
'architecture': inst.architecture,
|
|
|
|
|
'image_id': inst.image_id,
|
|
|
|
|
'key_name': inst.key_name,
|
|
|
|
|
'virtualization_type': inst.virtualization_type,
|
|
|
|
|
'placement': inst.placement,
|
|
|
|
|
'kernel': inst.kernel,
|
|
|
|
|
'ramdisk': inst.ramdisk,
|
|
|
|
|
'launch_time': inst.launch_time,
|
|
|
|
|
'instance_type': inst.instance_type,
|
|
|
|
|
'root_device_type': inst.root_device_type,
|
|
|
|
|
'root_device_name': inst.root_device_name,
|
|
|
|
|
'state': inst.state,
|
|
|
|
|
'hypervisor': inst.hypervisor
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_instances(module, ec2):
|
|
|
|
|
"""
|
|
|
|
|
Creates new instances
|
|
|
|
|
|
|
|
|
|
module : AnsbileModule object
|
|
|
|
|
ec2: authenticated ec2 connection object
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
A list of dictionaries with instance information
|
|
|
|
|
about the instances that were launched
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
key_name = module.params.get('key_name')
|
|
|
|
|
id = module.params.get('id')
|
|
|
|
|
group_name = module.params.get('group')
|
|
|
|
|
group_id = module.params.get('group_id')
|
|
|
|
|
region = module.params.get('region')
|
|
|
|
|
zone = module.params.get('zone')
|
|
|
|
|
instance_type = module.params.get('instance_type')
|
|
|
|
|
image = module.params.get('image')
|
|
|
|
@ -264,38 +340,12 @@ def main():
|
|
|
|
|
ramdisk = module.params.get('ramdisk')
|
|
|
|
|
wait = module.params.get('wait')
|
|
|
|
|
wait_timeout = int(module.params.get('wait_timeout'))
|
|
|
|
|
ec2_url = module.params.get('ec2_url')
|
|
|
|
|
ec2_secret_key = module.params.get('ec2_secret_key')
|
|
|
|
|
ec2_access_key = module.params.get('ec2_access_key')
|
|
|
|
|
placement_group = module.params.get('placement_group')
|
|
|
|
|
user_data = module.params.get('user_data')
|
|
|
|
|
instance_tags = module.params.get('instance_tags')
|
|
|
|
|
vpc_subnet_id = module.params.get('vpc_subnet_id')
|
|
|
|
|
private_ip = module.params.get('private_ip')
|
|
|
|
|
|
|
|
|
|
# allow eucarc environment variables to be used if ansible vars aren't set
|
|
|
|
|
if not ec2_url and 'EC2_URL' in os.environ:
|
|
|
|
|
ec2_url = os.environ['EC2_URL']
|
|
|
|
|
if not ec2_secret_key and 'EC2_SECRET_KEY' in os.environ:
|
|
|
|
|
ec2_secret_key = os.environ['EC2_SECRET_KEY']
|
|
|
|
|
if not ec2_access_key and 'EC2_ACCESS_KEY' in os.environ:
|
|
|
|
|
ec2_access_key = os.environ['EC2_ACCESS_KEY']
|
|
|
|
|
|
|
|
|
|
# If we have a region specified, connect to its endpoint.
|
|
|
|
|
if region:
|
|
|
|
|
try:
|
|
|
|
|
ec2 = boto.ec2.connect_to_region(region, aws_access_key_id=ec2_access_key, aws_secret_access_key=ec2_secret_key)
|
|
|
|
|
except boto.exception.NoAuthHandlerFound, e:
|
|
|
|
|
module.fail_json(msg = str(e))
|
|
|
|
|
# Otherwise, no region so we fallback to the old connection method
|
|
|
|
|
else:
|
|
|
|
|
try:
|
|
|
|
|
if ec2_url: # if we have an URL set, connect to the specified endpoint
|
|
|
|
|
ec2 = boto.connect_ec2_endpoint(ec2_url, ec2_access_key, ec2_secret_key)
|
|
|
|
|
else: # otherwise it's Amazon.
|
|
|
|
|
ec2 = boto.connect_ec2(ec2_access_key, ec2_secret_key)
|
|
|
|
|
except boto.exception.NoAuthHandlerFound, e:
|
|
|
|
|
module.fail_json(msg = str(e))
|
|
|
|
|
|
|
|
|
|
# Here we try to lookup the group name from the security group id - if group_id is set.
|
|
|
|
|
if group_id and group_name:
|
|
|
|
@ -398,33 +448,133 @@ def main():
|
|
|
|
|
running_instances.append(inst)
|
|
|
|
|
|
|
|
|
|
instance_dict_array = []
|
|
|
|
|
created_instance_ids = []
|
|
|
|
|
for inst in running_instances:
|
|
|
|
|
d = {
|
|
|
|
|
'id': inst.id,
|
|
|
|
|
'ami_launch_index': inst.ami_launch_index,
|
|
|
|
|
'private_ip': inst.private_ip_address,
|
|
|
|
|
'private_dns_name': inst.private_dns_name,
|
|
|
|
|
'public_ip': inst.ip_address,
|
|
|
|
|
'dns_name': inst.dns_name,
|
|
|
|
|
'public_dns_name': inst.public_dns_name,
|
|
|
|
|
'state_code': inst.state_code,
|
|
|
|
|
'architecture': inst.architecture,
|
|
|
|
|
'image_id': inst.image_id,
|
|
|
|
|
'key_name': inst.key_name,
|
|
|
|
|
'virtualization_type': inst.virtualization_type,
|
|
|
|
|
'placement': inst.placement,
|
|
|
|
|
'kernel': inst.kernel,
|
|
|
|
|
'ramdisk': inst.ramdisk,
|
|
|
|
|
'launch_time': inst.launch_time,
|
|
|
|
|
'instance_type': inst.instance_type,
|
|
|
|
|
'root_device_type': inst.root_device_type,
|
|
|
|
|
'root_device_name': inst.root_device_name,
|
|
|
|
|
'state': inst.state,
|
|
|
|
|
'hypervisor': inst.hypervisor
|
|
|
|
|
}
|
|
|
|
|
d = get_instance_info(inst)
|
|
|
|
|
created_instance_ids.append(inst.id)
|
|
|
|
|
instance_dict_array.append(d)
|
|
|
|
|
|
|
|
|
|
module.exit_json(changed=True, instances=instance_dict_array)
|
|
|
|
|
return (instance_dict_array, created_instance_ids)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def terminate_instances(module, ec2, instance_ids):
|
|
|
|
|
"""
|
|
|
|
|
Terminates a list of instances
|
|
|
|
|
|
|
|
|
|
module: Ansible module object
|
|
|
|
|
ec2: authenticated ec2 connection object
|
|
|
|
|
termination_list: a list of instances to terminate in the form of
|
|
|
|
|
[ {id: <inst-id>}, ..]
|
|
|
|
|
|
|
|
|
|
Returns a dictionary of instance information
|
|
|
|
|
about the instances terminated.
|
|
|
|
|
|
|
|
|
|
If the instance to be terminated is running
|
|
|
|
|
"changed" will be set to False.
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
changed = False
|
|
|
|
|
instance_dict_array = []
|
|
|
|
|
|
|
|
|
|
if not isinstance(instance_ids, list) or len(instance_ids) < 1:
|
|
|
|
|
module.fail_json(msg='instance_ids should be a list of instances, aborting')
|
|
|
|
|
|
|
|
|
|
terminated_instance_ids = []
|
|
|
|
|
for res in ec2.get_all_instances(instance_ids):
|
|
|
|
|
for inst in res.instances:
|
|
|
|
|
if inst.state == 'running':
|
|
|
|
|
terminated_instance_ids.append(inst.id)
|
|
|
|
|
instance_dict_array.append(get_instance_info(inst))
|
|
|
|
|
try:
|
|
|
|
|
ec2.terminate_instances([inst.id])
|
|
|
|
|
except EC2ResponseError as e:
|
|
|
|
|
module.fail_json(msg='Unable to terminate instance {0}, error: {1}'.format(inst.id, e))
|
|
|
|
|
changed = True
|
|
|
|
|
|
|
|
|
|
return (changed, instance_dict_array, terminated_instance_ids)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
module = AnsibleModule(
|
|
|
|
|
argument_spec = dict(
|
|
|
|
|
key_name = dict(aliases = ['keypair']),
|
|
|
|
|
id = dict(),
|
|
|
|
|
group = dict(type='list'),
|
|
|
|
|
group_id = dict(),
|
|
|
|
|
region = dict(choices=['eu-west-1', 'sa-east-1', 'us-east-1', 'ap-northeast-1', 'us-west-2', 'us-west-1', 'ap-southeast-1', 'ap-southeast-2']),
|
|
|
|
|
zone = dict(),
|
|
|
|
|
instance_type = dict(aliases=['type']),
|
|
|
|
|
image = dict(),
|
|
|
|
|
kernel = dict(),
|
|
|
|
|
count = dict(default='1'),
|
|
|
|
|
monitoring = dict(choices=BOOLEANS, default=False),
|
|
|
|
|
ramdisk = dict(),
|
|
|
|
|
wait = dict(choices=BOOLEANS, default=False),
|
|
|
|
|
wait_timeout = dict(default=300),
|
|
|
|
|
ec2_url = dict(aliases=['EC2_URL']),
|
|
|
|
|
ec2_secret_key = dict(aliases=['EC2_SECRET_KEY'], no_log=True),
|
|
|
|
|
ec2_access_key = dict(aliases=['EC2_ACCESS_KEY']),
|
|
|
|
|
placement_group = dict(),
|
|
|
|
|
user_data = dict(),
|
|
|
|
|
instance_tags = dict(),
|
|
|
|
|
vpc_subnet_id = dict(),
|
|
|
|
|
private_ip = dict(),
|
|
|
|
|
instance_ids = dict(type='list'),
|
|
|
|
|
state = dict(default='present'),
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
ec2_url = module.params.get('ec2_url')
|
|
|
|
|
ec2_secret_key = module.params.get('ec2_secret_key')
|
|
|
|
|
ec2_access_key = module.params.get('ec2_access_key')
|
|
|
|
|
region = module.params.get('region')
|
|
|
|
|
termination_list = module.params.get('termination_list')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# allow eucarc environment variables to be used if ansible vars aren't set
|
|
|
|
|
if not ec2_url and 'EC2_URL' in os.environ:
|
|
|
|
|
ec2_url = os.environ['EC2_URL']
|
|
|
|
|
if not ec2_secret_key and 'EC2_SECRET_KEY' in os.environ:
|
|
|
|
|
ec2_secret_key = os.environ['EC2_SECRET_KEY']
|
|
|
|
|
if not ec2_access_key and 'EC2_ACCESS_KEY' in os.environ:
|
|
|
|
|
ec2_access_key = os.environ['EC2_ACCESS_KEY']
|
|
|
|
|
|
|
|
|
|
# If we have a region specified, connect to its endpoint.
|
|
|
|
|
if region:
|
|
|
|
|
try:
|
|
|
|
|
ec2 = boto.ec2.connect_to_region(region, aws_access_key_id=ec2_access_key, aws_secret_access_key=ec2_secret_key)
|
|
|
|
|
except boto.exception.NoAuthHandlerFound, e:
|
|
|
|
|
module.fail_json(msg = str(e))
|
|
|
|
|
# Otherwise, no region so we fallback to the old connection method
|
|
|
|
|
else:
|
|
|
|
|
try:
|
|
|
|
|
if ec2_url: # if we have an URL set, connect to the specified endpoint
|
|
|
|
|
ec2 = boto.connect_ec2_endpoint(ec2_url, ec2_access_key, ec2_secret_key)
|
|
|
|
|
else: # otherwise it's Amazon.
|
|
|
|
|
ec2 = boto.connect_ec2(ec2_access_key, ec2_secret_key)
|
|
|
|
|
except boto.exception.NoAuthHandlerFound, e:
|
|
|
|
|
module.fail_json(msg = str(e))
|
|
|
|
|
|
|
|
|
|
if module.params.get('state') == 'absent':
|
|
|
|
|
instance_ids = module.params.get('instance_ids')
|
|
|
|
|
if not isinstance(instance_ids, list):
|
|
|
|
|
module.fail_json(msg='termination_list needs to be a list of instances to terminate')
|
|
|
|
|
|
|
|
|
|
(changed, instance_dict_array, new_instance_ids) = terminate_instances(module, ec2, instance_ids)
|
|
|
|
|
|
|
|
|
|
elif module.params.get('state') == 'present':
|
|
|
|
|
# Changed is always set to true when provisioning new instances
|
|
|
|
|
changed = True
|
|
|
|
|
if not module.params.get('key_name'):
|
|
|
|
|
module.fail_json(msg='key_name parameter is required for new instance')
|
|
|
|
|
if not module.params.get('image'):
|
|
|
|
|
module.fail_json(msg='image parameter is required for new instance')
|
|
|
|
|
(instance_dict_array, new_instance_ids) = create_instances(module, ec2)
|
|
|
|
|
|
|
|
|
|
module.exit_json(changed=True, instance_ids=new_instance_ids, instances=instance_dict_array)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# this is magic, see lib/ansible/module_common.py
|
|
|
|
|
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
|
|
|
|
|