#!/usr/bin/python # -*- coding: utf-8 -*- # # Copyright (c) 2017, René Moser # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'} DOCUMENTATION = r''' --- module: cs_vpn_connection short_description: Manages site-to-site VPN connections on Apache CloudStack based clouds. description: - Create and remove VPN connections. author: René Moser (@resmo) options: vpc: description: - Name of the VPC the VPN connection is related to. type: str required: true vpn_customer_gateway: description: - Name of the VPN customer gateway. type: str required: true passive: description: - State of the VPN connection. - Only considered when I(state=present). default: no type: bool force: description: - Activate the VPN gateway if not already activated on I(state=present). - Also see M(cs_vpn_gateway). default: no type: bool state: description: - State of the VPN connection. type: str default: present choices: [ present, absent ] zone: description: - Name of the zone the VPC is related to. - If not set, default zone is used. type: str domain: description: - Domain the VPN connection is related to. type: str account: description: - Account the VPN connection is related to. type: str project: description: - Name of the project the VPN connection is related to. type: str poll_async: description: - Poll async jobs until job has finished. default: yes type: bool extends_documentation_fragment: - community.general.cloudstack ''' EXAMPLES = r''' - name: Create a VPN connection with activated VPN gateway cs_vpn_connection: vpn_customer_gateway: my vpn connection vpc: my vpc delegate_to: localhost - name: Create a VPN connection and force VPN gateway activation cs_vpn_connection: vpn_customer_gateway: my vpn connection vpc: my vpc force: yes delegate_to: localhost - name: Remove a vpn connection cs_vpn_connection: vpn_customer_gateway: my vpn connection vpc: my vpc state: absent delegate_to: localhost ''' RETURN = r''' --- id: description: UUID of the VPN connection. returned: success type: str sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6 vpn_gateway_id: description: UUID of the VPN gateway. returned: success type: str sample: 04589590-ac63-93f5-4ffc-b698b8ac38b6 domain: description: Domain the VPN connection is related to. returned: success type: str sample: example domain account: description: Account the VPN connection is related to. returned: success type: str sample: example account project: description: Name of project the VPN connection is related to. returned: success type: str sample: Production created: description: Date the connection was created. returned: success type: str sample: 2014-12-01T14:57:57+0100 dpd: description: Whether dead pear detection is enabled or not. returned: success type: bool sample: true esp_lifetime: description: Lifetime in seconds of phase 2 VPN connection. returned: success type: int sample: 86400 esp_policy: description: IKE policy of the VPN connection. returned: success type: str sample: aes256-sha1;modp1536 force_encap: description: Whether encapsulation for NAT traversal is enforced or not. returned: success type: bool sample: true ike_lifetime: description: Lifetime in seconds of phase 1 VPN connection. returned: success type: int sample: 86400 ike_policy: description: ESP policy of the VPN connection. returned: success type: str sample: aes256-sha1;modp1536 cidrs: description: List of CIDRs of the customer gateway. returned: success type: list sample: [ 10.10.10.0/24 ] passive: description: Whether the connection is passive or not. returned: success type: bool sample: false public_ip: description: IP address of the VPN gateway. returned: success type: str sample: 10.100.212.10 gateway: description: IP address of the VPN customer gateway. returned: success type: str sample: 10.101.214.10 state: description: State of the VPN connection. returned: success type: str sample: Connected ''' from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.general.plugins.module_utils.cloudstack import ( AnsibleCloudStack, cs_argument_spec, cs_required_together ) class AnsibleCloudStackVpnConnection(AnsibleCloudStack): def __init__(self, module): super(AnsibleCloudStackVpnConnection, self).__init__(module) self.returns = { 'dpd': 'dpd', 'esplifetime': 'esp_lifetime', 'esppolicy': 'esp_policy', 'gateway': 'gateway', 'ikepolicy': 'ike_policy', 'ikelifetime': 'ike_lifetime', 'publicip': 'public_ip', 'passive': 'passive', 's2svpngatewayid': 'vpn_gateway_id', } self.vpn_customer_gateway = None def get_vpn_customer_gateway(self, key=None, identifier=None, refresh=False): if not refresh and self.vpn_customer_gateway: return self._get_by_key(key, self.vpn_customer_gateway) args = { 'account': self.get_account(key='name'), 'domainid': self.get_domain(key='id'), 'projectid': self.get_project(key='id'), 'fetch_list': True, } vpn_customer_gateway = identifier or self.module.params.get('vpn_customer_gateway') vcgws = self.query_api('listVpnCustomerGateways', **args) if vcgws: for vcgw in vcgws: if vpn_customer_gateway.lower() in [vcgw['id'], vcgw['name'].lower()]: self.vpn_customer_gateway = vcgw return self._get_by_key(key, self.vpn_customer_gateway) self.fail_json(msg="VPN customer gateway not found: %s" % vpn_customer_gateway) def get_vpn_gateway(self, key=None): args = { 'vpcid': self.get_vpc(key='id'), 'account': self.get_account(key='name'), 'domainid': self.get_domain(key='id'), 'projectid': self.get_project(key='id'), } vpn_gateways = self.query_api('listVpnGateways', **args) if vpn_gateways: return self._get_by_key(key, vpn_gateways['vpngateway'][0]) elif self.module.params.get('force'): if self.module.check_mode: return {} res = self.query_api('createVpnGateway', **args) vpn_gateway = self.poll_job(res, 'vpngateway') return self._get_by_key(key, vpn_gateway) self.fail_json(msg="VPN gateway not found and not forced to create one") def get_vpn_connection(self): args = { 'vpcid': self.get_vpc(key='id'), 'account': self.get_account(key='name'), 'domainid': self.get_domain(key='id'), 'projectid': self.get_project(key='id'), } vpn_conns = self.query_api('listVpnConnections', **args) if vpn_conns: for vpn_conn in vpn_conns['vpnconnection']: if self.get_vpn_customer_gateway(key='id') == vpn_conn['s2scustomergatewayid']: return vpn_conn def present_vpn_connection(self): vpn_conn = self.get_vpn_connection() args = { 's2scustomergatewayid': self.get_vpn_customer_gateway(key='id'), 's2svpngatewayid': self.get_vpn_gateway(key='id'), 'passive': self.module.params.get('passive'), } if not vpn_conn: self.result['changed'] = True if not self.module.check_mode: res = self.query_api('createVpnConnection', **args) poll_async = self.module.params.get('poll_async') if poll_async: vpn_conn = self.poll_job(res, 'vpnconnection') return vpn_conn def absent_vpn_connection(self): vpn_conn = self.get_vpn_connection() if vpn_conn: self.result['changed'] = True args = { 'id': vpn_conn['id'] } if not self.module.check_mode: res = self.query_api('deleteVpnConnection', **args) poll_async = self.module.params.get('poll_async') if poll_async: self.poll_job(res, 'vpnconnection') return vpn_conn def get_result(self, vpn_conn): super(AnsibleCloudStackVpnConnection, self).get_result(vpn_conn) if vpn_conn: if 'cidrlist' in vpn_conn: self.result['cidrs'] = vpn_conn['cidrlist'].split(',') or [vpn_conn['cidrlist']] # Ensure we return a bool self.result['force_encap'] = True if vpn_conn.get('forceencap') else False args = { 'key': 'name', 'identifier': vpn_conn['s2scustomergatewayid'], 'refresh': True, } self.result['vpn_customer_gateway'] = self.get_vpn_customer_gateway(**args) return self.result def main(): argument_spec = cs_argument_spec() argument_spec.update(dict( vpn_customer_gateway=dict(required=True), vpc=dict(required=True), domain=dict(), account=dict(), project=dict(), zone=dict(), passive=dict(type='bool', default=False), force=dict(type='bool', default=False), state=dict(choices=['present', 'absent'], default='present'), poll_async=dict(type='bool', default=True), )) module = AnsibleModule( argument_spec=argument_spec, required_together=cs_required_together(), supports_check_mode=True ) acs_vpn_conn = AnsibleCloudStackVpnConnection(module) state = module.params.get('state') if state == "absent": vpn_conn = acs_vpn_conn.absent_vpn_connection() else: vpn_conn = acs_vpn_conn.present_vpn_connection() result = acs_vpn_conn.get_result(vpn_conn) module.exit_json(**result) if __name__ == '__main__': main()