From 2ef6713abbfdd11507c6141a7415537d061c4f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Moser?= Date: Wed, 10 May 2017 14:55:14 +0200 Subject: [PATCH] cloudstack: new module cs_vpn_gateway (#23954) * cloudstack: new module cs_vpn_gateway * add integration tests for cs_vpn_gateway --- .../cloud/cloudstack/cs_vpn_gateway.py | 232 ++++++++++++++++++ .../integration/targets/cs_vpc/tasks/main.yml | 16 +- .../targets/cs_vpn_gateway/aliases | 2 + .../targets/cs_vpn_gateway/meta/main.yml | 3 + .../targets/cs_vpn_gateway/tasks/main.yml | 82 +++++++ 5 files changed, 327 insertions(+), 8 deletions(-) create mode 100644 lib/ansible/modules/cloud/cloudstack/cs_vpn_gateway.py create mode 100644 test/integration/targets/cs_vpn_gateway/aliases create mode 100644 test/integration/targets/cs_vpn_gateway/meta/main.yml create mode 100644 test/integration/targets/cs_vpn_gateway/tasks/main.yml diff --git a/lib/ansible/modules/cloud/cloudstack/cs_vpn_gateway.py b/lib/ansible/modules/cloud/cloudstack/cs_vpn_gateway.py new file mode 100644 index 00000000000..0694b82fd9c --- /dev/null +++ b/lib/ansible/modules/cloud/cloudstack/cs_vpn_gateway.py @@ -0,0 +1,232 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# (c) 2017, René Moser +# +# 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 . + +ANSIBLE_METADATA = {'metadata_version': '1.0', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: cs_vpn_gateway +short_description: Manages site-to-site VPN gateways on Apache CloudStack based clouds. +description: + - Creates and removes VPN site-to-site gateways. +version_added: "2.4" +author: "René Moser (@resmo)" +options: + vpc: + description: + - Name of the VPC. + required: true + state: + description: + - State of the VPN gateway. + required: false + default: "present" + choices: [ 'present', 'absent' ] + domain: + description: + - Domain the VPN gateway is related to. + required: false + default: null + account: + description: + - Account the VPN gateway is related to. + required: false + default: null + project: + description: + - Name of the project the VPN gateway is related to. + required: false + default: null + zone: + description: + - Name of the zone the VPC is related to. + - If not set, default zone is used. + required: false + default: null + poll_async: + description: + - Poll async jobs until job has finished. + required: false + default: true +extends_documentation_fragment: cloudstack +''' + +EXAMPLES = ''' +# Ensure a vpn gateway is present +- local_action: + module: cs_vpn_gateway + vpc: my VPC + +# Ensure a vpn gateway is absent +- local_action: + module: cs_vpn_gateway + vpc: my VPC + state: absent +''' + +RETURN = ''' +--- +id: + description: UUID of the VPN site-to-site gateway. + returned: success + type: string + sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6 +public_ip: + description: IP address of the VPN site-to-site gateway. + returned: success + type: string + sample: 10.100.212.10 +vpc: + description: Name of the VPC. + returned: success + type: string + sample: My VPC +domain: + description: Domain the VPN site-to-site gateway is related to. + returned: success + type: string + sample: example domain +account: + description: Account the VPN site-to-site gateway is related to. + returned: success + type: string + sample: example account +project: + description: Name of project the VPN site-to-site gateway is related to. + returned: success + type: string + sample: Production +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.cloudstack import ( + AnsibleCloudStack, + CloudStackException, + cs_argument_spec, + cs_required_together +) + + +class AnsibleCloudStackVpnGateway(AnsibleCloudStack): + + def __init__(self, module): + super(AnsibleCloudStackVpnGateway, self).__init__(module) + self.returns = { + 'publicip': 'public_ip' + } + + def get_vpn_gateway(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_gateways = self.cs.listVpnGateways(**args) + if vpn_gateways: + return vpn_gateways['vpngateway'][0] + return None + + def present_vpn_gateway(self): + vpn_gateway = self.get_vpn_gateway() + if not vpn_gateway: + self.result['changed'] = True + 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') + } + if not self.module.check_mode: + res = self.cs.createVpnGateway(**args) + if 'errortext' in res: + self.module.fail_json(msg="Failed: '%s'" % res['errortext']) + + poll_async = self.module.params.get('poll_async') + if poll_async: + vpn_gateway = self.poll_job(res, 'vpngateway') + + return vpn_gateway + + def absent_vpn_gateway(self): + vpn_gateway = self.get_vpn_gateway() + if vpn_gateway: + self.result['changed'] = True + args = { + 'id': vpn_gateway['id'] + } + if not self.module.check_mode: + res = self.cs.deleteVpnGateway(**args) + if 'errortext' in res: + self.module.fail_json(msg="Failed: '%s'" % res['errortext']) + + poll_async = self.module.params.get('poll_async') + if poll_async: + self.poll_job(res, 'vpngateway') + + return vpn_gateway + + def get_result(self, vpn_gateway): + super(AnsibleCloudStackVpnGateway, self).get_result(vpn_gateway) + if vpn_gateway: + self.result['vpc'] = self.get_vpc(key='name') + return self.result + + +def main(): + argument_spec = cs_argument_spec() + argument_spec.update(dict( + vpc=dict(required=True), + state=dict(choices=['present', 'absent'], default='present'), + domain=dict(), + account=dict(), + project=dict(), + zone=dict(), + poll_async=dict(type='bool', default=True), + )) + + module = AnsibleModule( + argument_spec=argument_spec, + required_together=cs_required_together(), + supports_check_mode=True + ) + + try: + acs_vpn_gw = AnsibleCloudStackVpnGateway(module) + + state = module.params.get('state') + if state == "absent": + vpn_gateway = acs_vpn_gw.absent_vpn_gateway() + else: + vpn_gateway = acs_vpn_gw.present_vpn_gateway() + + result = acs_vpn_gw.get_result(vpn_gateway) + + except CloudStackException as e: + module.fail_json(msg='CloudStackException: %s' % str(e)) + + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/cs_vpc/tasks/main.yml b/test/integration/targets/cs_vpc/tasks/main.yml index 72702698d51..5646e208fe2 100644 --- a/test/integration/targets/cs_vpc/tasks/main.yml +++ b/test/integration/targets/cs_vpc/tasks/main.yml @@ -9,22 +9,22 @@ that: - vpc|success -- name: test fail missing name +- name: test fail missing name of vpc cs_vpc: ignore_errors: true register: vpc -- name: verify test fail missing name +- name: verify test fail missing name of vpc assert: that: - vpc|failed - - 'vpc.msg == "missing required arguments: name"' + - "vpc.msg.startswith('missing required arguments: ')" -- name: test fail missing cidr +- name: test fail missing cidr for vpc cs_vpc: name: "{{ cs_resource_prefix }}_vpc" ignore_errors: true register: vpc -- name: verify test fail missing name +- name: verify test fail missing cidr for vpc assert: that: - vpc|failed @@ -68,7 +68,7 @@ cidr: 10.10.0.0/16 zone: "{{ cs_common_zone_adv }}" register: vpc -- name: verify test create vpc idempotence +- name: verify test create vpc idempotence2 assert: that: - vpc|success @@ -84,7 +84,7 @@ cidr: 10.10.0.0/16 zone: "{{ cs_common_zone_adv }}" register: vpc -- name: verify test create vpc +- name: verify test update vpc assert: that: - vpc|success @@ -100,7 +100,7 @@ cidr: 10.10.0.0/16 zone: "{{ cs_common_zone_adv }}" register: vpc -- name: verify test create vpc +- name: verify test update vpc idempotence assert: that: - vpc|success diff --git a/test/integration/targets/cs_vpn_gateway/aliases b/test/integration/targets/cs_vpn_gateway/aliases new file mode 100644 index 00000000000..ba249b99d73 --- /dev/null +++ b/test/integration/targets/cs_vpn_gateway/aliases @@ -0,0 +1,2 @@ +cloud/cs +posix/ci/cloud/cs diff --git a/test/integration/targets/cs_vpn_gateway/meta/main.yml b/test/integration/targets/cs_vpn_gateway/meta/main.yml new file mode 100644 index 00000000000..e9a5b9eeaef --- /dev/null +++ b/test/integration/targets/cs_vpn_gateway/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - cs_common diff --git a/test/integration/targets/cs_vpn_gateway/tasks/main.yml b/test/integration/targets/cs_vpn_gateway/tasks/main.yml new file mode 100644 index 00000000000..94fd5e8510d --- /dev/null +++ b/test/integration/targets/cs_vpn_gateway/tasks/main.yml @@ -0,0 +1,82 @@ +--- +- name: setup vpc + cs_vpc: + name: "{{ cs_resource_prefix }}_vpc" + display_text: "{{ cs_resource_prefix }}_display_text" + cidr: 10.10.0.0/16 + zone: "{{ cs_common_zone_adv }}" + register: vpc +- name: verify setup vpc + assert: + that: + - vpc|success + +- name: setup vpn gateway absent + cs_vpn_gateway: + vpc: "{{ cs_resource_prefix }}_vpc" + zone: "{{ cs_common_zone_adv }}" + state: absent + register: vpn_gateway +- name: verify setup vpn gateway absent + assert: + that: + - vpn_gateway|success + +- name: test fail missing param vpc for vpn gateway + cs_vpn_gateway: + ignore_errors: true + register: vpn_gateway +- name: verify test fail missing param vpc for vpn gateway + assert: + that: + - vpn_gateway|failed + - "vpn_gateway.msg.startswith('missing required arguments: ')" + +- name: test create vpn gateway + cs_vpn_gateway: + vpc: "{{ cs_resource_prefix }}_vpc" + zone: "{{ cs_common_zone_adv }}" + register: vpn_gateway +- name: verify test create vpn gateway + assert: + that: + - vpn_gateway|success + - vpn_gateway|changed + - vpn_gateway.vpc == "{{ cs_resource_prefix }}_vpc" + +- name: test create vpn gateway idempotence + cs_vpn_gateway: + vpc: "{{ cs_resource_prefix }}_vpc" + zone: "{{ cs_common_zone_adv }}" + register: vpn_gateway +- name: verify test create vpn gateway idempotence + assert: + that: + - vpn_gateway|success + - not vpn_gateway|changed + - vpn_gateway.vpc == "{{ cs_resource_prefix }}_vpc" + +- name: test remove vpn gateway + cs_vpn_gateway: + vpc: "{{ cs_resource_prefix }}_vpc" + zone: "{{ cs_common_zone_adv }}" + state: absent + register: vpn_gateway +- name: verify test remove vpn gateway + assert: + that: + - vpn_gateway|success + - vpn_gateway|changed + - vpn_gateway.vpc == "{{ cs_resource_prefix }}_vpc" + +- name: test remove vpn gateway idempotence + cs_vpn_gateway: + vpc: "{{ cs_resource_prefix }}_vpc" + zone: "{{ cs_common_zone_adv }}" + state: absent + register: vpn_gateway +- name: verify test remove vpn gateway idempotence + assert: + that: + - vpn_gateway|success + - not vpn_gateway|changed