1#!/usr/bin/python
2#
3# This is a free software: you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation, either version 3 of the License, or
6# (at your option) any later version.
7#
8# This Ansible library is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this library.  If not, see <http://www.gnu.org/licenses/>.
15
16ANSIBLE_METADATA = {'metadata_version': '1.1',
17                    'status': ['preview'],
18                    'supported_by': 'community'}
19
20
21DOCUMENTATION = '''
22---
23module: ec2_customer_gateway
24short_description: Manage an AWS customer gateway
25description:
26    - Manage an AWS customer gateway
27version_added: "2.2"
28author: Michael Baydoun (@MichaelBaydoun)
29requirements: [ botocore, boto3 ]
30notes:
31    - You cannot create more than one customer gateway with the same IP address. If you run an identical request more than one time, the
32      first request creates the customer gateway, and subsequent requests return information about the existing customer gateway. The subsequent
33      requests do not create new customer gateway resources.
34    - Return values contain customer_gateway and customer_gateways keys which are identical dicts. You should use
35      customer_gateway. See U(https://github.com/ansible/ansible-modules-extras/issues/2773) for details.
36options:
37  bgp_asn:
38    description:
39      - Border Gateway Protocol (BGP) Autonomous System Number (ASN), required when state=present.
40  ip_address:
41    description:
42      - Internet-routable IP address for customers gateway, must be a static address.
43    required: true
44  name:
45    description:
46      - Name of the customer gateway.
47    required: true
48  routing:
49    description:
50      - The type of routing.
51    choices: ['static', 'dynamic']
52    default: dynamic
53    version_added: '2.4'
54  state:
55    description:
56      - Create or terminate the Customer Gateway.
57    default: present
58    choices: [ 'present', 'absent' ]
59extends_documentation_fragment:
60    - aws
61    - ec2
62'''
63
64EXAMPLES = '''
65
66# Create Customer Gateway
67- ec2_customer_gateway:
68    bgp_asn: 12345
69    ip_address: 1.2.3.4
70    name: IndianapolisOffice
71    region: us-east-1
72  register: cgw
73
74# Delete Customer Gateway
75- ec2_customer_gateway:
76    ip_address: 1.2.3.4
77    name: IndianapolisOffice
78    state: absent
79    region: us-east-1
80  register: cgw
81'''
82
83RETURN = '''
84gateway.customer_gateways:
85    description: details about the gateway that was created.
86    returned: success
87    type: complex
88    contains:
89        bgp_asn:
90            description: The Border Gateway Autonomous System Number.
91            returned: when exists and gateway is available.
92            sample: 65123
93            type: str
94        customer_gateway_id:
95            description: gateway id assigned by amazon.
96            returned: when exists and gateway is available.
97            sample: cgw-cb6386a2
98            type: str
99        ip_address:
100            description: ip address of your gateway device.
101            returned: when exists and gateway is available.
102            sample: 1.2.3.4
103            type: str
104        state:
105            description: state of gateway.
106            returned: when gateway exists and is available.
107            state: available
108            type: str
109        tags:
110            description: any tags on the gateway.
111            returned: when gateway exists and is available, and when tags exist.
112            state: available
113            type: str
114        type:
115            description: encryption type.
116            returned: when gateway exists and is available.
117            sample: ipsec.1
118            type: str
119'''
120
121try:
122    from botocore.exceptions import ClientError
123    HAS_BOTOCORE = True
124except ImportError:
125    HAS_BOTOCORE = False
126
127try:
128    import boto3
129    HAS_BOTO3 = True
130except ImportError:
131    HAS_BOTO3 = False
132
133from ansible.module_utils.basic import AnsibleModule
134from ansible.module_utils.ec2 import (boto3_conn, AWSRetry, camel_dict_to_snake_dict,
135                                      ec2_argument_spec, get_aws_connection_info)
136
137
138class Ec2CustomerGatewayManager:
139
140    def __init__(self, module):
141        self.module = module
142
143        try:
144            region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True)
145            if not region:
146                module.fail_json(msg="Region must be specified as a parameter, in EC2_REGION or AWS_REGION environment variables or in boto configuration file")
147            self.ec2 = boto3_conn(module, conn_type='client', resource='ec2', region=region, endpoint=ec2_url, **aws_connect_kwargs)
148        except ClientError as e:
149            module.fail_json(msg=e.message)
150
151    @AWSRetry.jittered_backoff(delay=2, max_delay=30, retries=6, catch_extra_error_codes=['IncorrectState'])
152    def ensure_cgw_absent(self, gw_id):
153        response = self.ec2.delete_customer_gateway(
154            DryRun=False,
155            CustomerGatewayId=gw_id
156        )
157        return response
158
159    def ensure_cgw_present(self, bgp_asn, ip_address):
160        if not bgp_asn:
161            bgp_asn = 65000
162        response = self.ec2.create_customer_gateway(
163            DryRun=False,
164            Type='ipsec.1',
165            PublicIp=ip_address,
166            BgpAsn=bgp_asn,
167        )
168        return response
169
170    def tag_cgw_name(self, gw_id, name):
171        response = self.ec2.create_tags(
172            DryRun=False,
173            Resources=[
174                gw_id,
175            ],
176            Tags=[
177                {
178                    'Key': 'Name',
179                    'Value': name
180                },
181            ]
182        )
183        return response
184
185    def describe_gateways(self, ip_address):
186        response = self.ec2.describe_customer_gateways(
187            DryRun=False,
188            Filters=[
189                {
190                    'Name': 'state',
191                    'Values': [
192                        'available',
193                    ]
194                },
195                {
196                    'Name': 'ip-address',
197                    'Values': [
198                        ip_address,
199                    ]
200                }
201            ]
202        )
203        return response
204
205
206def main():
207    argument_spec = ec2_argument_spec()
208    argument_spec.update(
209        dict(
210            bgp_asn=dict(required=False, type='int'),
211            ip_address=dict(required=True),
212            name=dict(required=True),
213            routing=dict(default='dynamic', choices=['dynamic', 'static']),
214            state=dict(default='present', choices=['present', 'absent']),
215        )
216    )
217
218    module = AnsibleModule(argument_spec=argument_spec,
219                           supports_check_mode=True,
220                           required_if=[
221                               ('routing', 'dynamic', ['bgp_asn'])
222                           ]
223                           )
224
225    if not HAS_BOTOCORE:
226        module.fail_json(msg='botocore is required.')
227
228    if not HAS_BOTO3:
229        module.fail_json(msg='boto3 is required.')
230
231    gw_mgr = Ec2CustomerGatewayManager(module)
232
233    name = module.params.get('name')
234
235    existing = gw_mgr.describe_gateways(module.params['ip_address'])
236
237    results = dict(changed=False)
238    if module.params['state'] == 'present':
239        if existing['CustomerGateways']:
240            existing['CustomerGateway'] = existing['CustomerGateways'][0]
241            results['gateway'] = existing
242            if existing['CustomerGateway']['Tags']:
243                tag_array = existing['CustomerGateway']['Tags']
244                for key, value in enumerate(tag_array):
245                    if value['Key'] == 'Name':
246                        current_name = value['Value']
247                        if current_name != name:
248                            results['name'] = gw_mgr.tag_cgw_name(
249                                results['gateway']['CustomerGateway']['CustomerGatewayId'],
250                                module.params['name'],
251                            )
252                            results['changed'] = True
253        else:
254            if not module.check_mode:
255                results['gateway'] = gw_mgr.ensure_cgw_present(
256                    module.params['bgp_asn'],
257                    module.params['ip_address'],
258                )
259                results['name'] = gw_mgr.tag_cgw_name(
260                    results['gateway']['CustomerGateway']['CustomerGatewayId'],
261                    module.params['name'],
262                )
263            results['changed'] = True
264
265    elif module.params['state'] == 'absent':
266        if existing['CustomerGateways']:
267            existing['CustomerGateway'] = existing['CustomerGateways'][0]
268            results['gateway'] = existing
269            if not module.check_mode:
270                results['gateway'] = gw_mgr.ensure_cgw_absent(
271                    existing['CustomerGateway']['CustomerGatewayId']
272                )
273            results['changed'] = True
274
275    pretty_results = camel_dict_to_snake_dict(results)
276    module.exit_json(**pretty_results)
277
278
279if __name__ == '__main__':
280    main()
281