mirror of https://github.com/ansible/ansible.git
commit
dd085c7e9f
@ -0,0 +1,243 @@
|
||||
#!/usr/bin/python -tt
|
||||
# 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: cloudformation
|
||||
short_description: create a AWS CloudFormation stack
|
||||
description:
|
||||
- Launches an AWS CloudFormation stack and waits for it complete.
|
||||
version_added: "1.1"
|
||||
options:
|
||||
stack_name:
|
||||
description:
|
||||
- name of the cloudformation stack
|
||||
required: true
|
||||
default: null
|
||||
aliases: []
|
||||
disable_rollback:
|
||||
description:
|
||||
- If a stacks fails to form, rollback will remove the stack
|
||||
required: false
|
||||
default: false
|
||||
aliases: []
|
||||
template_parameters:
|
||||
description:
|
||||
- a list of hashes of all the template variables for the stack
|
||||
required: true
|
||||
default: null
|
||||
aliases: []
|
||||
region:
|
||||
description:
|
||||
- The AWS region the stack will be launched in
|
||||
required: true
|
||||
default: null
|
||||
aliases: []
|
||||
state:
|
||||
description:
|
||||
- If state is "present", stack will be created. If state is "present" and if stack exists and template has changed, it will be updated.
|
||||
If state is present, stack will be removed.
|
||||
required: true
|
||||
default: null
|
||||
aliases: []
|
||||
template:
|
||||
description:
|
||||
- the path of the cloudformation template
|
||||
required: true
|
||||
default: null
|
||||
aliases: []
|
||||
wait_for:
|
||||
description:
|
||||
- Wait while the stack is being created/updated/deleted.
|
||||
required: false
|
||||
default: true
|
||||
aliases: []
|
||||
|
||||
examples:
|
||||
|
||||
tasks:
|
||||
- name: launch ansible cloudformation example
|
||||
cloudformation: >
|
||||
stack_name="ansible-cloudformation" state=present
|
||||
region=us-east-1 disable_rollback=true
|
||||
template=files/cloudformation-example.json
|
||||
args:
|
||||
template_parameters:
|
||||
KeyName: jmartin
|
||||
DiskType: ephemeral
|
||||
InstanceType: m1.small
|
||||
ClusterSize: 3
|
||||
|
||||
requirements: [ "boto" ]
|
||||
author: James S. Martin
|
||||
'''
|
||||
|
||||
import boto.cloudformation.connection
|
||||
import json
|
||||
|
||||
|
||||
class Region:
|
||||
def __init__(self, region):
|
||||
self.name = region
|
||||
self.endpoint = 'cloudformation.%s.amazonaws.com' % region
|
||||
|
||||
|
||||
def boto_exception(err):
|
||||
|
||||
if hasattr(err, 'error_message'):
|
||||
error = err.error_message
|
||||
elif hasattr(err, 'message'):
|
||||
error = err.message
|
||||
else:
|
||||
error = '%s: %s' % (Exception, err)
|
||||
try:
|
||||
error_msg = json.loads(error)
|
||||
except:
|
||||
error_msg = {'Error': error}
|
||||
return error_msg
|
||||
|
||||
|
||||
def stack_operation(cfn, stack_name, operation):
|
||||
existed = []
|
||||
result = {}
|
||||
operation_complete = False
|
||||
while operation_complete == False:
|
||||
try:
|
||||
stack = cfn.describe_stacks(stack_name)[0]
|
||||
existed.append('yes')
|
||||
except:
|
||||
if 'yes' in existed:
|
||||
result = {'changed': True, 'output': 'Stack Deleted'}
|
||||
result['events'] = map(str, list(stack.describe_events()))
|
||||
else:
|
||||
result = {'changed': True, 'output': 'Stack Not Found'}
|
||||
break
|
||||
if '%s_COMPLETE' % operation == stack.stack_status:
|
||||
result['changed'] = True
|
||||
result['events'] = map(str, list(stack.describe_events()))
|
||||
result['output'] = 'Stack %s complete' % operation
|
||||
break
|
||||
elif '%s_FAILED' % operation == stack.stack_status:
|
||||
result['changed'] = False
|
||||
result['events'] = map(str, list(stack.describe_events()))
|
||||
result['output'] = 'Stack %s failed' % operation
|
||||
break
|
||||
else:
|
||||
time.sleep(5)
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
stack_name=dict(required=True),
|
||||
template_parameters=dict(required=False),
|
||||
region=dict(required=True,
|
||||
choices=['ap-northeast-1', 'ap-southeast-1',
|
||||
'ap-southeast-2', 'eu-west-1',
|
||||
'sa-east-1', 'us-east-1', 'us-west-1',
|
||||
'us-west-2']),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
template=dict(default=None, required=True),
|
||||
disable_rollback=dict(default=False),
|
||||
wait_for=dict(default=True)
|
||||
)
|
||||
)
|
||||
|
||||
wait_for = module.params['wait_for']
|
||||
state = module.params['state']
|
||||
stack_name = module.params['stack_name']
|
||||
region = Region(module.params['region'])
|
||||
template_body = open(module.params['template'], 'r').read()
|
||||
disable_rollback = module.params['disable_rollback']
|
||||
template_parameters = module.params['template_parameters']
|
||||
|
||||
template_parameters_tup = [(k, v) for k, v in template_parameters.items()]
|
||||
stack_outputs = {}
|
||||
stack_outputs[module.params['region']] = {}
|
||||
stack_outputs[module.params['region']][stack_name] = {}
|
||||
|
||||
try:
|
||||
cfn = boto.cloudformation.connection.CloudFormationConnection(
|
||||
region=region)
|
||||
except boto.exception.NoAuthHandlerFound, e:
|
||||
module.fail_json(msg=str(e))
|
||||
update = False
|
||||
stack_events = []
|
||||
result = {}
|
||||
operation = None
|
||||
output = ''
|
||||
|
||||
if state == 'present':
|
||||
try:
|
||||
cfn.create_stack(stack_name, parameters=template_parameters_tup,
|
||||
template_body=template_body,
|
||||
disable_rollback=disable_rollback,
|
||||
capabilities=['CAPABILITY_IAM'])
|
||||
operation = 'CREATE'
|
||||
except Exception, err:
|
||||
error_msg = boto_exception(err)
|
||||
if error_msg['Error']['Code'] == 'AlreadyExistsException':
|
||||
update = True
|
||||
else:
|
||||
result = {'changed': False, 'output': error_msg}
|
||||
module.fail_json(**result)
|
||||
if not update:
|
||||
result = stack_operation(cfn, stack_name, operation)
|
||||
if update:
|
||||
try:
|
||||
cfn.update_stack(stack_name, parameters=template_parameters_tup,
|
||||
template_body=template_body,
|
||||
disable_rollback=disable_rollback,
|
||||
capabilities=['CAPABILITY_IAM'])
|
||||
operation = 'UPDATE'
|
||||
except Exception, err:
|
||||
error_msg = boto_exception(err)
|
||||
if error_msg['Error']['Message'] == 'No updates are to be performed.':
|
||||
output = error_msg['Error']['Message']
|
||||
result = {'changed': False, 'output': output}
|
||||
if operation == 'UPDATE':
|
||||
result = stack_operation(cfn, stack_name, operation)
|
||||
|
||||
if state == 'present' or update:
|
||||
stack = cfn.describe_stacks(stack_name)[0]
|
||||
for output in stack.outputs:
|
||||
stack_outputs[module.params['region']][stack_name][
|
||||
output.key] = output.value
|
||||
result['stack_outputs'] = stack_outputs
|
||||
|
||||
# absent state is different because of the way delete_stack works.
|
||||
# problem is it it doesn't give an error if stack isn't found
|
||||
# so must describe the stack first
|
||||
|
||||
if state == 'absent':
|
||||
try:
|
||||
cfn.describe_stacks(stack_name)
|
||||
operation = 'DELETE'
|
||||
except Exception, err:
|
||||
error_msg = boto_exception(err)
|
||||
result = {'changed': False, 'output': error_msg}
|
||||
module.fail_json(result)
|
||||
if operation == 'DELETE':
|
||||
cfn.delete_stack(stack_name)
|
||||
result = stack_operation(cfn, stack_name, operation)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
# this is magic, see lib/ansible/module_common.py
|
||||
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
|
||||
|
||||
main()
|
Loading…
Reference in New Issue