b4fc3cdb9a
Default is set to 300 seconds, which is AWS default. This PR fixes a bug (#7898) where instances created within an autoscaling group using the `ec2_asg` module gets immediately terminated because the `health_check_period` is set to 0, which causes the instance to be checked without having the time to actually boot. Adding `health_check_type` is needed because you may want to check your instance health against an ELB instead of just EC2 default cloudwatch.
312 lines
11 KiB
Python
Executable file
312 lines
11 KiB
Python
Executable file
#!/usr/bin/python
|
|
# This file is part of Ansible
|
|
#
|
|
# Ansible is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Ansible is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
DOCUMENTATION = """
|
|
---
|
|
module: ec2_asg
|
|
short_description: Create or delete AWS Autoscaling Groups
|
|
description:
|
|
- Can create or delete AWS Autoscaling Groups
|
|
- Works with the ec2_lc module to manage Launch Configurations
|
|
version_added: "1.6"
|
|
author: Gareth Rushgrove
|
|
options:
|
|
state:
|
|
description:
|
|
- register or deregister the instance
|
|
required: true
|
|
choices: ['present', 'absent']
|
|
name:
|
|
description:
|
|
- Unique name for group to be created or deleted
|
|
required: true
|
|
load_balancers:
|
|
description:
|
|
- List of ELB names to use for the group
|
|
required: false
|
|
availability_zones:
|
|
description:
|
|
- List of availability zone names in which to create the group. Defaults to all the availability zones in the region if vpc_zone_identifier is not set.
|
|
required: false
|
|
launch_config_name:
|
|
description:
|
|
- Name of the Launch configuration to use for the group. See the ec2_lc module for managing these.
|
|
required: false
|
|
min_size:
|
|
description:
|
|
- Minimum number of instances in group
|
|
required: false
|
|
max_size:
|
|
description:
|
|
- Maximum number of instances in group
|
|
required: false
|
|
desired_capacity:
|
|
description:
|
|
- Desired number of instances in group
|
|
required: false
|
|
region:
|
|
description:
|
|
- The AWS region to use. If not specified then the value of the EC2_REGION environment variable, if any, is used.
|
|
required: false
|
|
aliases: ['aws_region', 'ec2_region']
|
|
vpc_zone_identifier:
|
|
description:
|
|
- List of VPC subnets to use
|
|
required: false
|
|
default: None
|
|
tags:
|
|
description:
|
|
- List of tag dictionaries to use. Required keys are 'key', 'value'. Optional key is 'propagate_at_launch', which defaults to true.
|
|
required: false
|
|
default: None
|
|
version_added: "1.7"
|
|
health_check_period:
|
|
description:
|
|
- Length of time in seconds after a new EC2 instance comes into service that Auto Scaling starts checking its health.
|
|
required: false
|
|
default: 500 seconds
|
|
version_added: "1.7"
|
|
health_check_type:
|
|
description:
|
|
- The service you want the health status from, Amazon EC2 or Elastic Load Balancer.
|
|
required: false
|
|
default: EC2
|
|
version_added: "1.7"
|
|
extends_documentation_fragment: aws
|
|
"""
|
|
|
|
EXAMPLES = '''
|
|
- ec2_asg:
|
|
name: special
|
|
load_balancers: 'lb1,lb2'
|
|
availability_zones: 'eu-west-1a,eu-west-1b'
|
|
launch_config_name: 'lc-1'
|
|
min_size: 1
|
|
max_size: 10
|
|
desired_capacity: 5
|
|
vpc_zone_identifier: 'subnet-abcd1234,subnet-1a2b3c4d'
|
|
tags:
|
|
- key: environment
|
|
value: production
|
|
propagate_at_launch: no
|
|
|
|
'''
|
|
|
|
import sys
|
|
import time
|
|
|
|
from ansible.module_utils.basic import *
|
|
from ansible.module_utils.ec2 import *
|
|
|
|
try:
|
|
import boto.ec2.autoscale
|
|
from boto.ec2.autoscale import AutoScaleConnection, AutoScalingGroup, Tag
|
|
from boto.exception import BotoServerError
|
|
except ImportError:
|
|
print "failed=True msg='boto required for this module'"
|
|
sys.exit(1)
|
|
|
|
ASG_ATTRIBUTES = ('launch_config_name', 'max_size', 'min_size', 'desired_capacity',
|
|
'vpc_zone_identifier', 'availability_zones')
|
|
|
|
def enforce_required_arguments(module):
|
|
''' As many arguments are not required for autoscale group deletion
|
|
they cannot be mandatory arguments for the module, so we enforce
|
|
them here '''
|
|
missing_args = []
|
|
for arg in ('min_size', 'max_size', 'launch_config_name'):
|
|
if module.params[arg] is None:
|
|
missing_args.append(arg)
|
|
if missing_args:
|
|
module.fail_json(msg="Missing required arguments for autoscaling group create/update: %s" % ",".join(missing_args))
|
|
|
|
|
|
def get_properties(autoscaling_group):
|
|
properties = dict((attr, getattr(autoscaling_group, attr)) for attr in ASG_ATTRIBUTES)
|
|
if autoscaling_group.instances:
|
|
properties['instances'] = [i.instance_id for i in autoscaling_group.instances]
|
|
properties['load_balancers'] = autoscaling_group.load_balancers
|
|
return properties
|
|
|
|
|
|
def create_autoscaling_group(connection, module):
|
|
|
|
group_name = module.params.get('name')
|
|
load_balancers = module.params['load_balancers']
|
|
availability_zones = module.params['availability_zones']
|
|
launch_config_name = module.params.get('launch_config_name')
|
|
min_size = module.params['min_size']
|
|
max_size = module.params['max_size']
|
|
desired_capacity = module.params.get('desired_capacity')
|
|
vpc_zone_identifier = module.params.get('vpc_zone_identifier')
|
|
set_tags = module.params.get('tags')
|
|
health_check_period = module.params.get('health_check_period')
|
|
health_check_type = module.params.get('health_check_type')
|
|
|
|
as_groups = connection.get_all_groups(names=[group_name])
|
|
|
|
if not vpc_zone_identifier and not availability_zones:
|
|
region, ec2_url, aws_connect_params = get_aws_connection_info(module)
|
|
try:
|
|
ec2_connection = connect_to_aws(boto.ec2, region, **aws_connect_params)
|
|
except boto.exception.NoAuthHandlerFound, e:
|
|
module.fail_json(msg=str(e))
|
|
|
|
asg_tags = []
|
|
for tag in set_tags:
|
|
asg_tags.append(Tag(key=tag.get('key'),
|
|
value=tag.get('value'),
|
|
propagate_at_launch=bool(tag.get('propagate_at_launch', True)),
|
|
resource_id=group_name))
|
|
|
|
if not as_groups:
|
|
if not vpc_zone_identifier and not availability_zones:
|
|
availability_zones = module.params['availability_zones'] = [zone.name for zone in ec2_connection.get_all_zones()]
|
|
enforce_required_arguments(module)
|
|
launch_configs = connection.get_all_launch_configurations(names=[launch_config_name])
|
|
ag = AutoScalingGroup(
|
|
group_name=group_name,
|
|
load_balancers=load_balancers,
|
|
availability_zones=availability_zones,
|
|
launch_config=launch_configs[0],
|
|
min_size=min_size,
|
|
max_size=max_size,
|
|
desired_capacity=desired_capacity,
|
|
vpc_zone_identifier=vpc_zone_identifier,
|
|
connection=connection,
|
|
tags=asg_tags,
|
|
health_check_period=health_check_period,
|
|
health_check_type=health_check_type)
|
|
|
|
try:
|
|
connection.create_auto_scaling_group(ag)
|
|
asg_properties = get_properties(ag)
|
|
module.exit_json(changed=True, **asg_properties)
|
|
except BotoServerError, e:
|
|
module.fail_json(msg=str(e))
|
|
else:
|
|
as_group = as_groups[0]
|
|
changed = False
|
|
for attr in ASG_ATTRIBUTES:
|
|
if module.params.get(attr) and getattr(as_group, attr) != module.params.get(attr):
|
|
changed = True
|
|
setattr(as_group, attr, module.params.get(attr))
|
|
|
|
if len(set_tags) > 0:
|
|
existing_tags = as_group.tags
|
|
existing_tag_map = dict((tag.key, tag) for tag in existing_tags)
|
|
for tag in set_tags:
|
|
if ( not tag['key'] in existing_tag_map or
|
|
existing_tag_map[tag['key']].value != tag['value'] or
|
|
('propagate_at_launch' in tag and
|
|
existing_tag_map[tag['key']].propagate_at_launch != tag['propagate_at_launch']) ):
|
|
|
|
changed = True
|
|
continue
|
|
if changed:
|
|
connection.create_or_update_tags(asg_tags)
|
|
|
|
# handle loadbalancers separately because None != []
|
|
load_balancers = module.params.get('load_balancers') or []
|
|
if load_balancers and as_group.load_balancers != load_balancers:
|
|
changed = True
|
|
as_group.load_balancers = module.params.get('load_balancers')
|
|
|
|
try:
|
|
if changed:
|
|
as_group.update()
|
|
asg_properties = get_properties(as_group)
|
|
module.exit_json(changed=changed, **asg_properties)
|
|
except BotoServerError, e:
|
|
module.fail_json(msg=str(e))
|
|
|
|
result = as_groups[0]
|
|
module.exit_json(changed=changed, name=result.name,
|
|
autoscaling_group_arn=result.autoscaling_group_arn,
|
|
availability_zones=result.availability_zones,
|
|
created_time=str(result.created_time),
|
|
default_cooldown=result.default_cooldown,
|
|
health_check_period=result.health_check_period,
|
|
health_check_type=result.health_check_type,
|
|
instance_id=result.instance_id,
|
|
instances=[instance.instance_id for instance in result.instances],
|
|
launch_config_name=result.launch_config_name,
|
|
load_balancers=result.load_balancers,
|
|
min_size=result.min_size, max_size=result.max_size,
|
|
placement_group=result.placement_group,
|
|
tags=result.tags,
|
|
termination_policies=result.termination_policies,
|
|
vpc_zone_identifier=result.vpc_zone_identifier)
|
|
|
|
|
|
def delete_autoscaling_group(connection, module):
|
|
group_name = module.params.get('name')
|
|
groups = connection.get_all_groups(names=[group_name])
|
|
if groups:
|
|
group = groups[0]
|
|
group.shutdown_instances()
|
|
|
|
instances = True
|
|
while instances:
|
|
groups = connection.get_all_groups()
|
|
for group in groups:
|
|
if group.name == group_name:
|
|
if not group.instances:
|
|
instances = False
|
|
time.sleep(10)
|
|
|
|
group.delete()
|
|
module.exit_json(changed=True)
|
|
else:
|
|
module.exit_json(changed=False)
|
|
|
|
|
|
def main():
|
|
argument_spec = ec2_argument_spec()
|
|
argument_spec.update(
|
|
dict(
|
|
name=dict(required=True, type='str'),
|
|
load_balancers=dict(type='list'),
|
|
availability_zones=dict(type='list'),
|
|
launch_config_name=dict(type='str'),
|
|
min_size=dict(type='int'),
|
|
max_size=dict(type='int'),
|
|
desired_capacity=dict(type='int'),
|
|
vpc_zone_identifier=dict(type='str'),
|
|
state=dict(default='present', choices=['present', 'absent']),
|
|
tags=dict(type='list', default=[]),
|
|
health_check_period=dict(type='int', default=300),
|
|
health_check_type=dict(default='EC2', chices=['EC2', 'ELB']),
|
|
)
|
|
)
|
|
module = AnsibleModule(argument_spec=argument_spec)
|
|
|
|
state = module.params.get('state')
|
|
|
|
region, ec2_url, aws_connect_params = get_aws_connection_info(module)
|
|
try:
|
|
connection = connect_to_aws(boto.ec2.autoscale, region, **aws_connect_params)
|
|
if not connection:
|
|
module.fail_json(msg="failed to connect to AWS for the given region: %s" % str(region))
|
|
except boto.exception.NoAuthHandlerFound, e:
|
|
module.fail_json(msg=str(e))
|
|
|
|
if state == 'present':
|
|
create_autoscaling_group(connection, module)
|
|
elif state == 'absent':
|
|
delete_autoscaling_group(connection, module)
|
|
|
|
main()
|