From 526ee48c0dca015bb2b3e97359db870f0d98065a Mon Sep 17 00:00:00 2001 From: Nick Aslanidis Date: Mon, 22 Feb 2016 16:16:54 +1000 Subject: [PATCH 1/9] New AWS module for managing ec2 VPC virtual gateways --- cloud/amazon/ec2_vpc_vgw.py | 554 ++++++++++++++++++++++++++++++++++++ 1 file changed, 554 insertions(+) create mode 100644 cloud/amazon/ec2_vpc_vgw.py diff --git a/cloud/amazon/ec2_vpc_vgw.py b/cloud/amazon/ec2_vpc_vgw.py new file mode 100644 index 00000000000..0ce52b76ff8 --- /dev/null +++ b/cloud/amazon/ec2_vpc_vgw.py @@ -0,0 +1,554 @@ +#!/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 . + +DOCUMENTATION = ''' +module: ec2_vpc_vgw +short_description: Create and delete AWS VPN Virtual Gateways. +description: + - Creates AWS VPN Virtual Gateways + - Deletes AWS VPN Virtual Gateways + - Attaches Virtual Gateways to VPCs + - Detaches Virtual Gateways from VPCs +version_added: "2.1" +requirements: [ boto3 ] +options: + state: + description: + - present to ensure resource is created. + - absent to remove resource + required: false + default: present + choices: [ "present", "absent"] + name: + description: + - name of the vgw to be created or deleted + required: + - true when combined with a state of 'present' + - false when combined with a state of 'absent' + type: + description: + - type of the virtual gateway to be created + required: + - true when combined with a state of 'present' + - false when combined with a state of 'absent' + vpn_gateway_id: + description: + - vpn gateway id of an existing virtual gateway + required: false + vpc_id: + description: + - the vpc-id of a vpc to attach or detach + required: false + wait_timeout: + description: + - number of seconds to wait for status during vpc attach and detach + required: false + default: 320 + tags: + description: + - dictionary of resource tags of the form: { tag1: value1, tag2: value2 } + required: false +author: Nick Aslanidis (@naslanidis) +extends_documentation_fragment: aws +''' + +EXAMPLES = ''' +- name: Create a new vgw attached to a specific VPC + ec2_vpc_vgw: + state: present + region: ap-southeast-2 + profile: personal + vpc_id: vpc-12345678 + name: personal-testing + type: ipsec.1 + register: created_vgw + +- name: Create a new unattached vgw + ec2_vpc_vgw: + state: present + region: ap-southeast-2 + profile: personal + name: personal-testing + type: ipsec.1 + tags: + environment: production + owner: ABC + register: created_vgw + +- name: Remove a new vgw using the name + ec2_vpc_vgw: + state: absent + region: ap-southeast-2 + profile: personal + name: personal-testing + type: ipsec.1 + register: deleted_vgw + +- name: Remove a new vgw using the vpn_gateway_id + ec2_vpc_vgw: + state: absent + region: ap-southeast-2 + profile: personal + vpn_gateway_id: vgw-3a9aa123 + register: deleted_vgw +''' + +try: + import json + import time + import botocore + import boto3 + HAS_BOTO3 = True +except ImportError: + HAS_BOTO3 = False + + +def wait_for_status(client, module, vpn_gateway_id, status): + polling_increment_secs = 15 + max_retries = (module.params.get('wait_timeout') / polling_increment_secs) + status_achieved = False + + for x in range(0, max_retries): + try: + response = find_vgw(client, module, vpn_gateway_id) + if response[0]['VpcAttachments'][0]['State'] == status: + status_achieved = True + break + else: + time.sleep(polling_increment_secs) + except botocore.exceptions.ClientError as e: + module.fail_json(msg=str(e)) + + result = response + return status_achieved, result + + +def attach_vgw(client, module, vpn_gateway_id): + params = dict() + params['VpcId'] = module.params.get('vpc_id') + + try: + response = client.attach_vpn_gateway(VpnGatewayId=vpn_gateway_id, VpcId=params['VpcId']) + except botocore.exceptions.ClientError as e: + module.fail_json(msg=str(e)) + + status_achieved, vgw = wait_for_status(client, module, [vpn_gateway_id], 'attached') + if not status_achieved: + module.fail_json(msg='Error waiting for vpc to attach to vgw - please check the AWS console') + + result = response + return result + + +def detach_vgw(client, module, vpn_gateway_id, vpc_id=None): + params = dict() + params['VpcId'] = module.params.get('vpc_id') + + if vpc_id: + try: + response = client.detach_vpn_gateway(VpnGatewayId=vpn_gateway_id, VpcId=vpc_id) + except botocore.exceptions.ClientError as e: + module.fail_json(msg=str(e)) + else: + try: + response = client.detach_vpn_gateway(VpnGatewayId=vpn_gateway_id, VpcId=params['VpcId']) + except botocore.exceptions.ClientError as e: + module.fail_json(msg=str(e)) + + status_achieved, vgw = wait_for_status(client, module, [vpn_gateway_id], 'detached') + if not status_achieved: + module.fail_json(msg='Error waiting for vpc to detach from vgw - please check the AWS console') + + result = response + return result + + +def create_vgw(client, module): + params = dict() + params['Type'] = module.params.get('type') + + try: + response = client.create_vpn_gateway(Type=params['Type']) + except botocore.exceptions.ClientError as e: + module.fail_json(msg=str(e)) + + result = response + return result + + +def delete_vgw(client, module, vpn_gateway_id): + + try: + response = client.delete_vpn_gateway(VpnGatewayId=vpn_gateway_id) + except botocore.exceptions.ClientError as e: + module.fail_json(msg=str(e)) + + #return the deleted VpnGatewayId as this is not included in the above response + result = vpn_gateway_id + return result + + +def create_tags(client, module, vpn_gateway_id): + params = dict() + + try: + response = client.create_tags(Resources=[vpn_gateway_id],Tags=load_tags(module)) + except botocore.exceptions.ClientError as e: + module.fail_json(msg=str(e)) + + result = response + return result + + +def delete_tags(client, module, vpn_gateway_id, tags_to_delete=None): + params = dict() + + if tags_to_delete: + try: + response = client.delete_tags(Resources=[vpn_gateway_id], Tags=tags_to_delete) + except botocore.exceptions.ClientError as e: + module.fail_json(msg=str(e)) + else: + try: + response = client.delete_tags(Resources=[vpn_gateway_id]) + except botocore.exceptions.ClientError as e: + module.fail_json(msg=str(e)) + + result = response + return result + + +def load_tags(module): + tags = [] + + if module.params.get('tags'): + for name, value in module.params.get('tags').iteritems(): + tags.append({'Key': name, 'Value': str(value)}) + tags.append({'Key': "Name", 'Value': module.params.get('name')}) + else: + tags.append({'Key': "Name", 'Value': module.params.get('name')}) + return tags + + +def find_tags(client, module, resource_id=None): + + if resource_id: + try: + response = client.describe_tags(Filters=[ + {'Name': 'resource-id', 'Values': [resource_id]} + ]) + except botocore.exceptions.ClientError as e: + module.fail_json(msg=str(e)) + + result = response + return result + + +def check_tags(client, module, existing_vgw, vpn_gateway_id): + params = dict() + params['Tags'] = module.params.get('tags') + vgw = existing_vgw + changed = False + tags_list = {} + + #format tags for comparison + for tags in existing_vgw[0]['Tags']: + if tags['Key'] != 'Name': + tags_list[tags['Key']] = tags['Value'] + + # if existing tags don't match the tags arg, delete existing and recreate with new list + if params['Tags'] != None and tags_list != params['Tags']: + delete_tags(client, module, vpn_gateway_id) + create_tags(client, module, vpn_gateway_id) + vgw = find_vgw(client, module) + changed = True + + #if no tag args are supplied, delete any existing tags with the exception of the name tag + if params['Tags'] == None and tags_list != {}: + tags_to_delete = [] + for tags in existing_vgw[0]['Tags']: + if tags['Key'] != 'Name': + tags_to_delete.append(tags) + + delete_tags(client, module, vpn_gateway_id, tags_to_delete) + vgw = find_vgw(client, module) + changed = True + + return vgw, changed + + +def find_vpc(client, module): + params = dict() + params['vpc_id'] = module.params.get('vpc_id') + + if params['vpc_id']: + try: + response = client.describe_vpcs(VpcIds=[params['vpc_id']]) + except botocore.exceptions.ClientError as e: + module.fail_json(msg=str(e)) + + result = response + return result + + +def find_vgw(client, module, vpn_gateway_id=None): + params = dict() + params['Name'] = module.params.get('name') + params['Type'] = module.params.get('type') + params['State'] = module.params.get('state') + + if params['State'] == 'present': + try: + response = client.describe_vpn_gateways(Filters=[ + {'Name': 'type', 'Values': [params['Type']]}, + {'Name': 'tag:Name', 'Values': [params['Name']]} + ]) + except botocore.exceptions.ClientError as e: + module.fail_json(msg=str(e)) + + else: + if vpn_gateway_id: + try: + response = client.describe_vpn_gateways(VpnGatewayIds=vpn_gateway_id) + except botocore.exceptions.ClientError as e: + module.fail_json(msg=str(e)) + + else: + try: + response = client.describe_vpn_gateways(Filters=[ + {'Name': 'type', 'Values': [params['Type']]}, + {'Name': 'tag:Name', 'Values': [params['Name']]} + ]) + except botocore.exceptions.ClientError as e: + module.fail_json(msg=str(e)) + + result = response['VpnGateways'] + return result + + +def ensure_vgw_present(client, module): + +# If an existing vgw name and type matches our args, then a match is considered to have been +# found and we will not create another vgw. + + changed = False + params = dict() + result = dict() + params['Name'] = module.params.get('name') + params['VpcId'] = module.params.get('vpc_id') + params['Type'] = module.params.get('type') + params['Tags'] = module.params.get('tags') + params['VpnGatewayIds'] = module.params.get('vpn_gateway_id') + + # Check that a name argument has been supplied. + if not module.params.get('name'): + module.fail_json(msg='A name is required when a status of \'present\' is suppled') + + # check if a gateway matching our module args already exists + existing_vgw = find_vgw(client, module) + + if existing_vgw != [] and existing_vgw[0]['State'] != 'deleted': + vpn_gateway_id = existing_vgw[0]['VpnGatewayId'] + vgw, changed = check_tags(client, module, existing_vgw, vpn_gateway_id) + + if not changed: + + # if a vpc_id was provided, check if it exists and if it's attached + if params['VpcId']: + + # check that the vpc_id exists. If not, an exception is thrown + vpc = find_vpc(client, module) + current_vpc_attachments = existing_vgw[0]['VpcAttachments'] + + if current_vpc_attachments != [] and current_vpc_attachments[0]['State'] == 'attached': + if current_vpc_attachments[0]['VpcId'] == params['VpcId'] and current_vpc_attachments[0]['State'] == 'attached': + changed = False + else: + + # detach the existing vpc from the virtual gateway + vpc_to_detach = current_vpc_attachments[0]['VpcId'] + detach_vgw(client, module, vpn_gateway_id, vpc_to_detach) + time.sleep(5) + attached_vgw = attach_vgw(client, module, vpn_gateway_id) + changed = True + else: + # attach the vgw to the supplied vpc + attached_vgw = attach_vgw(client, module, vpn_gateway_id) + changed = True + + # if params['VpcId'] is not provided, check the vgw is attached to a vpc. if so, detach it. + else: + existing_vgw = find_vgw(client, module, [vpn_gateway_id]) + + if existing_vgw[0]['VpcAttachments'] != []: + if existing_vgw[0]['VpcAttachments'][0]['State'] == 'attached': + # detach the vpc from the vgw + vpc_to_detach = existing_vgw[0]['VpcAttachments'][0]['VpcId'] + detach_vgw(client, module, vpn_gateway_id, vpc_to_detach) + changed = True + + vgw = find_vgw(client, module, [vpn_gateway_id]) + + else: + # create a new vgw + new_vgw = create_vgw(client, module) + changed = True + vpn_gateway_id = new_vgw['VpnGateway']['VpnGatewayId'] + + # tag the new virtual gateway + create_tags(client, module, vpn_gateway_id) + + # return current state of the vgw + vgw = find_vgw(client, module, [vpn_gateway_id]) + + # if a vpc-id was supplied, attempt to attach it to the vgw + if params['VpcId']: + attached_vgw = attach_vgw(client, module, vpn_gateway_id) + changed = True + vgw = find_vgw(client, module, [vpn_gateway_id]) + + result = vgw + return changed, result + + +def ensure_vgw_absent(client, module): + +# If an existing vgw name and type matches our args, then a match is considered to have been +# found and we will take steps to delete it. + + changed = False + params = dict() + result = dict() + params['Name'] = module.params.get('name') + params['VpcId'] = module.params.get('vpc_id') + params['Type'] = module.params.get('type') + params['Tags'] = module.params.get('tags') + params['VpnGatewayIds'] = module.params.get('vpn_gateway_id') + + # check if a gateway matching our module args already exists + if params['VpnGatewayIds']: + existing_vgw_with_id = find_vgw(client, module, [params['VpnGatewayIds']]) + if existing_vgw_with_id != [] and existing_vgw_with_id[0]['State'] != 'deleted': + existing_vgw = existing_vgw_with_id + if existing_vgw[0]['VpcAttachments'] != [] and existing_vgw[0]['VpcAttachments'][0]['State'] == 'attached': + if params['VpcId']: + if params['VpcId'] != existing_vgw[0]['VpcAttachments'][0]['VpcId']: + module.fail_json(msg='The vpc-id provided does not match the vpc-id currently attached - please check the AWS console') + + else: + # detach the vpc from the vgw + detach_vgw(client, module, params['VpnGatewayIds'], params['VpcId']) + deleted_vgw = delete_vgw(client, module, params['VpnGatewayIds']) + changed = True + + else: + # attempt to detach any attached vpcs + vpc_to_detach = existing_vgw[0]['VpcAttachments'][0]['VpcId'] + detach_vgw(client, module, params['VpnGatewayIds'], vpc_to_detach) + deleted_vgw = delete_vgw(client, module, params['VpnGatewayIds']) + changed = True + + else: + # no vpc's are attached so attempt to delete the vgw + deleted_vgw = delete_vgw(client, module, params['VpnGatewayIds']) + changed = True + + else: + changed = False + deleted_vgw = None + + else: + #Check that a name and type argument has been supplied if no vgw-id + if not module.params.get('name') or not module.params.get('type'): + module.fail_json(msg='A name and type is required when no vgw-id and a status of \'absent\' is suppled') + + existing_vgw = find_vgw(client, module) + if existing_vgw != [] and existing_vgw[0]['State'] != 'deleted': + vpn_gateway_id = existing_vgw[0]['VpnGatewayId'] + if existing_vgw[0]['VpcAttachments'] != [] and existing_vgw[0]['VpcAttachments'][0]['State'] == 'attached': + if params['VpcId']: + if params['VpcId'] != existing_vgw[0]['VpcAttachments'][0]['VpcId']: + module.fail_json(msg='The vpc-id provided does not match the vpc-id currently attached - please check the AWS console') + + else: + # detach the vpc from the vgw + detach_vgw(client, module, vpn_gateway_id, params['VpcId']) + + #now that the vpc has been detached, delete the vgw + deleted_vgw = delete_vgw(client, module, vpn_gateway_id) + changed = True + + else: + # attempt to detach any attached vpcs + vpc_to_detach = existing_vgw[0]['VpcAttachments'][0]['VpcId'] + detach_vgw(client, module, vpn_gateway_id, vpc_to_detach) + changed = True + + #now that the vpc has been detached, delete the vgw + deleted_vgw = delete_vgw(client, module, vpn_gateway_id) + + else: + # no vpc's are attached so attempt to delete the vgw + deleted_vgw = delete_vgw(client, module, vpn_gateway_id) + changed = True + + else: + changed = False + deleted_vgw = None + + result = deleted_vgw + return changed, result + + +def main(): + argument_spec = ec2_argument_spec() + argument_spec.update(dict( + state=dict(default='present', choices=['present', 'absent']), + region=dict(required=True), + name=dict(), + vpn_gateway_id=dict(), + vpc_id=dict(), + wait_timeout=dict(type='int', default=320, required=False), + type=dict(), + tags=dict(), + ) + ) + module = AnsibleModule(argument_spec=argument_spec) + + if not HAS_BOTO3: + module.fail_json(msg='json and boto3 is required.') + + state = module.params.get('state').lower() + + try: + region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) + client = boto3_conn(module, conn_type='client', resource='ec2', region=region, endpoint=ec2_url, **aws_connect_kwargs) + except botocore.exceptions.NoCredentialsError, e: + module.fail_json(msg="Can't authorize connection - "+str(e)) + + if state == 'present': + (changed, results) = ensure_vgw_present(client, module) + else: + (changed, results) = ensure_vgw_absent(client, module) + module.exit_json(changed=changed, result=results) + + +# import module snippets +from ansible.module_utils.basic import * +from ansible.module_utils.ec2 import * + +if __name__ == '__main__': + main() From 20df1189b98e78afbe2cf910e29c6da514263099 Mon Sep 17 00:00:00 2001 From: naslanidis Date: Mon, 22 Feb 2016 20:32:18 +1000 Subject: [PATCH 2/9] Fixed issue with tag changes affecting vpc attach --- cloud/amazon/ec2_vpc_vgw.py | 80 ++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/cloud/amazon/ec2_vpc_vgw.py b/cloud/amazon/ec2_vpc_vgw.py index 0ce52b76ff8..4c7bebd323a 100644 --- a/cloud/amazon/ec2_vpc_vgw.py +++ b/cloud/amazon/ec2_vpc_vgw.py @@ -148,7 +148,7 @@ def attach_vgw(client, module, vpn_gateway_id): status_achieved, vgw = wait_for_status(client, module, [vpn_gateway_id], 'attached') if not status_achieved: module.fail_json(msg='Error waiting for vpc to attach to vgw - please check the AWS console') - + result = response return result @@ -195,7 +195,7 @@ def delete_vgw(client, module, vpn_gateway_id): response = client.delete_vpn_gateway(VpnGatewayId=vpn_gateway_id) except botocore.exceptions.ClientError as e: module.fail_json(msg=str(e)) - + #return the deleted VpnGatewayId as this is not included in the above response result = vpn_gateway_id return result @@ -318,7 +318,7 @@ def find_vgw(client, module, vpn_gateway_id=None): ]) except botocore.exceptions.ClientError as e: module.fail_json(msg=str(e)) - + else: if vpn_gateway_id: try: @@ -334,7 +334,7 @@ def find_vgw(client, module, vpn_gateway_id=None): ]) except botocore.exceptions.ClientError as e: module.fail_json(msg=str(e)) - + result = response['VpnGateways'] return result @@ -363,44 +363,44 @@ def ensure_vgw_present(client, module): if existing_vgw != [] and existing_vgw[0]['State'] != 'deleted': vpn_gateway_id = existing_vgw[0]['VpnGatewayId'] vgw, changed = check_tags(client, module, existing_vgw, vpn_gateway_id) - - if not changed: - - # if a vpc_id was provided, check if it exists and if it's attached - if params['VpcId']: - - # check that the vpc_id exists. If not, an exception is thrown - vpc = find_vpc(client, module) - current_vpc_attachments = existing_vgw[0]['VpcAttachments'] - - if current_vpc_attachments != [] and current_vpc_attachments[0]['State'] == 'attached': - if current_vpc_attachments[0]['VpcId'] == params['VpcId'] and current_vpc_attachments[0]['State'] == 'attached': - changed = False - else: - - # detach the existing vpc from the virtual gateway - vpc_to_detach = current_vpc_attachments[0]['VpcId'] - detach_vgw(client, module, vpn_gateway_id, vpc_to_detach) - time.sleep(5) - attached_vgw = attach_vgw(client, module, vpn_gateway_id) - changed = True + + # if a vpc_id was provided, check if it exists and if it's attached + if params['VpcId']: + + # check that the vpc_id exists. If not, an exception is thrown + vpc = find_vpc(client, module) + current_vpc_attachments = existing_vgw[0]['VpcAttachments'] + + if current_vpc_attachments != [] and current_vpc_attachments[0]['State'] == 'attached': + if current_vpc_attachments[0]['VpcId'] == params['VpcId'] and current_vpc_attachments[0]['State'] == 'attached': + changed = False else: - # attach the vgw to the supplied vpc + + # detach the existing vpc from the virtual gateway + vpc_to_detach = current_vpc_attachments[0]['VpcId'] + detach_vgw(client, module, vpn_gateway_id, vpc_to_detach) + time.sleep(5) attached_vgw = attach_vgw(client, module, vpn_gateway_id) + vgw = find_vgw(client, module, [vpn_gateway_id]) + changed = True + else: + # attach the vgw to the supplied vpc + attached_vgw = attach_vgw(client, module, vpn_gateway_id) + vgw = find_vgw(client, module, [vpn_gateway_id]) + changed = True + + # if params['VpcId'] is not provided, check the vgw is attached to a vpc. if so, detach it. + else: + existing_vgw = find_vgw(client, module, [vpn_gateway_id]) + + if existing_vgw[0]['VpcAttachments'] != []: + if existing_vgw[0]['VpcAttachments'][0]['State'] == 'attached': + # detach the vpc from the vgw + vpc_to_detach = existing_vgw[0]['VpcAttachments'][0]['VpcId'] + detach_vgw(client, module, vpn_gateway_id, vpc_to_detach) changed = True - # if params['VpcId'] is not provided, check the vgw is attached to a vpc. if so, detach it. - else: - existing_vgw = find_vgw(client, module, [vpn_gateway_id]) - - if existing_vgw[0]['VpcAttachments'] != []: - if existing_vgw[0]['VpcAttachments'][0]['State'] == 'attached': - # detach the vpc from the vgw - vpc_to_detach = existing_vgw[0]['VpcAttachments'][0]['VpcId'] - detach_vgw(client, module, vpn_gateway_id, vpc_to_detach) - changed = True - - vgw = find_vgw(client, module, [vpn_gateway_id]) + vgw = find_vgw(client, module, [vpn_gateway_id]) else: # create a new vgw @@ -468,7 +468,7 @@ def ensure_vgw_absent(client, module): else: changed = False - deleted_vgw = None + deleted_vgw = "Nothing to do" else: #Check that a name and type argument has been supplied if no vgw-id @@ -551,4 +551,4 @@ from ansible.module_utils.basic import * from ansible.module_utils.ec2 import * if __name__ == '__main__': - main() + main() \ No newline at end of file From b4163e52c55c789897cf0f1ebde357160716b9ab Mon Sep 17 00:00:00 2001 From: naslanidis Date: Mon, 22 Feb 2016 21:29:29 +1000 Subject: [PATCH 3/9] Added return section to the documentation --- cloud/amazon/ec2_vpc_vgw.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cloud/amazon/ec2_vpc_vgw.py b/cloud/amazon/ec2_vpc_vgw.py index 4c7bebd323a..59336b11607 100644 --- a/cloud/amazon/ec2_vpc_vgw.py +++ b/cloud/amazon/ec2_vpc_vgw.py @@ -106,6 +106,13 @@ EXAMPLES = ''' register: deleted_vgw ''' +RETURN = ''' +result: + description: The result of the create, or delete action. + returned: success + type: dictionary +''' + try: import json import time From 26e2c1bf116be4392a3ee993a8e60a15f643aaed Mon Sep 17 00:00:00 2001 From: Nick Aslanidis Date: Sun, 6 Mar 2016 16:09:14 +1000 Subject: [PATCH 4/9] updated extends_documentation_fragment and final cr --- cloud/amazon/ec2_vpc_vgw.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cloud/amazon/ec2_vpc_vgw.py b/cloud/amazon/ec2_vpc_vgw.py index 59336b11607..7e5893ee27f 100644 --- a/cloud/amazon/ec2_vpc_vgw.py +++ b/cloud/amazon/ec2_vpc_vgw.py @@ -62,7 +62,9 @@ options: - dictionary of resource tags of the form: { tag1: value1, tag2: value2 } required: false author: Nick Aslanidis (@naslanidis) -extends_documentation_fragment: aws +extends_documentation_fragment: + - aws + - ec2 ''' EXAMPLES = ''' @@ -558,4 +560,5 @@ from ansible.module_utils.basic import * from ansible.module_utils.ec2 import * if __name__ == '__main__': - main() \ No newline at end of file + main() + From fa8eb632f8f1a1e77ec71a5c7041e53f6fad815d Mon Sep 17 00:00:00 2001 From: Nick Aslanidis Date: Sun, 6 Mar 2016 16:15:38 +1000 Subject: [PATCH 5/9] attempt to fix doc fragment --- cloud/amazon/ec2_vpc_vgw.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cloud/amazon/ec2_vpc_vgw.py b/cloud/amazon/ec2_vpc_vgw.py index 7e5893ee27f..e641ed711be 100644 --- a/cloud/amazon/ec2_vpc_vgw.py +++ b/cloud/amazon/ec2_vpc_vgw.py @@ -63,8 +63,8 @@ options: required: false author: Nick Aslanidis (@naslanidis) extends_documentation_fragment: - - aws - - ec2 + - aws + - ec2 ''' EXAMPLES = ''' From 9eb9d1c74b4b22cc09a10bcdd776e889690f7eb2 Mon Sep 17 00:00:00 2001 From: Nick Aslanidis Date: Sun, 6 Mar 2016 16:36:13 +1000 Subject: [PATCH 6/9] corrected required to be bool instead of list --- cloud/amazon/ec2_vpc_vgw.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/cloud/amazon/ec2_vpc_vgw.py b/cloud/amazon/ec2_vpc_vgw.py index e641ed711be..58108c2dbf0 100644 --- a/cloud/amazon/ec2_vpc_vgw.py +++ b/cloud/amazon/ec2_vpc_vgw.py @@ -35,15 +35,11 @@ options: name: description: - name of the vgw to be created or deleted - required: - - true when combined with a state of 'present' - - false when combined with a state of 'absent' + required: false type: description: - type of the virtual gateway to be created - required: - - true when combined with a state of 'present' - - false when combined with a state of 'absent' + required: false vpn_gateway_id: description: - vpn gateway id of an existing virtual gateway @@ -63,8 +59,8 @@ options: required: false author: Nick Aslanidis (@naslanidis) extends_documentation_fragment: - - aws - - ec2 + - aws + - ec2 ''' EXAMPLES = ''' From 2f56b3c8b45bd6eeb01342825026475071fa232e Mon Sep 17 00:00:00 2001 From: Nick Aslanidis Date: Sun, 6 Mar 2016 16:41:34 +1000 Subject: [PATCH 7/9] corrected invalid tag description for CI checks --- cloud/amazon/ec2_vpc_vgw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloud/amazon/ec2_vpc_vgw.py b/cloud/amazon/ec2_vpc_vgw.py index 58108c2dbf0..6c8a80e9872 100644 --- a/cloud/amazon/ec2_vpc_vgw.py +++ b/cloud/amazon/ec2_vpc_vgw.py @@ -55,7 +55,7 @@ options: default: 320 tags: description: - - dictionary of resource tags of the form: { tag1: value1, tag2: value2 } + - dictionary of resource tags required: false author: Nick Aslanidis (@naslanidis) extends_documentation_fragment: From ea8547c86ad8ad1f4c7fdef6549ac64c28c002be Mon Sep 17 00:00:00 2001 From: Brad Davidson Date: Fri, 29 Apr 2016 20:29:14 +0000 Subject: [PATCH 8/9] Fix argument spec for type and tags; return VGW info instead of raw response --- cloud/amazon/ec2_vpc_vgw.py | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/cloud/amazon/ec2_vpc_vgw.py b/cloud/amazon/ec2_vpc_vgw.py index 6c8a80e9872..466b81c8762 100644 --- a/cloud/amazon/ec2_vpc_vgw.py +++ b/cloud/amazon/ec2_vpc_vgw.py @@ -40,6 +40,7 @@ options: description: - type of the virtual gateway to be created required: false + choices: [ "ipsec.1" ] vpn_gateway_id: description: - vpn gateway id of an existing virtual gateway @@ -57,6 +58,8 @@ options: description: - dictionary of resource tags required: false + default: null + aliases: [ "resource_tags" ] author: Nick Aslanidis (@naslanidis) extends_documentation_fragment: - aws @@ -120,6 +123,26 @@ try: except ImportError: HAS_BOTO3 = False +def get_vgw_info(vgws): + if not isinstance(vgws, list): + return + + for vgw in vgws: + vgw_info = { + 'id': vgw['VpnGatewayId'], + 'type': vgw['Type'], + 'state': vgw['State'], + 'vpc_id': None, + 'tags': dict() + } + + for tag in vgw['Tags']: + vgw_info['tags'][tag['Key']] = tag['Value'] + + if len(vgw['VpcAttachments']) != 0: + vgw_info['vpc_id'] = vgw['VpcAttachments'][0]['VpcId'] + + return vgw_info def wait_for_status(client, module, vpn_gateway_id, status): polling_increment_secs = 15 @@ -425,7 +448,7 @@ def ensure_vgw_present(client, module): changed = True vgw = find_vgw(client, module, [vpn_gateway_id]) - result = vgw + result = get_vgw_info(vgw) return changed, result @@ -514,7 +537,7 @@ def ensure_vgw_absent(client, module): changed = False deleted_vgw = None - result = deleted_vgw + result = get_vgw_info(deleted_vgw) return changed, result @@ -526,9 +549,9 @@ def main(): name=dict(), vpn_gateway_id=dict(), vpc_id=dict(), - wait_timeout=dict(type='int', default=320, required=False), - type=dict(), - tags=dict(), + wait_timeout=dict(type='int', default=320), + type=dict(default='ipsec.1', choices=['ipsec.1']), + tags=dict(default=None, required=False, type='dict', aliases=['resource_tags']), ) ) module = AnsibleModule(argument_spec=argument_spec) @@ -548,7 +571,7 @@ def main(): (changed, results) = ensure_vgw_present(client, module) else: (changed, results) = ensure_vgw_absent(client, module) - module.exit_json(changed=changed, result=results) + module.exit_json(changed=changed, vgw=results) # import module snippets From 8be42e067625f1d35dcaae10e19021e8cebdc0ce Mon Sep 17 00:00:00 2001 From: Nick Aslanidis Date: Sat, 7 May 2016 21:00:23 +1000 Subject: [PATCH 9/9] corrected version to 2.2. Ensure no vpc-id is returned if detached --- cloud/amazon/ec2_vpc_vgw.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cloud/amazon/ec2_vpc_vgw.py b/cloud/amazon/ec2_vpc_vgw.py index 466b81c8762..9861e7d0b7e 100644 --- a/cloud/amazon/ec2_vpc_vgw.py +++ b/cloud/amazon/ec2_vpc_vgw.py @@ -22,7 +22,7 @@ description: - Deletes AWS VPN Virtual Gateways - Attaches Virtual Gateways to VPCs - Detaches Virtual Gateways from VPCs -version_added: "2.1" +version_added: "2.2" requirements: [ boto3 ] options: state: @@ -139,7 +139,7 @@ def get_vgw_info(vgws): for tag in vgw['Tags']: vgw_info['tags'][tag['Key']] = tag['Value'] - if len(vgw['VpcAttachments']) != 0: + if len(vgw['VpcAttachments']) != 0 and vgw['VpcAttachments'][0]['State'] == 'attached': vgw_info['vpc_id'] = vgw['VpcAttachments'][0]['VpcId'] return vgw_info @@ -537,7 +537,7 @@ def ensure_vgw_absent(client, module): changed = False deleted_vgw = None - result = get_vgw_info(deleted_vgw) + result = deleted_vgw return changed, result