From 3ca3163af17f4caf00d02b3bbad4b969ecfdb442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Moser?= Date: Mon, 15 May 2017 10:57:38 +0200 Subject: [PATCH] cloudstack: add new module cs_network_acl (#24609) --- .../cloud/cloudstack/cs_network_acl.py | 224 ++++++++++++++++++ .../targets/cs_network_acl/aliases | 2 + .../targets/cs_network_acl/meta/main.yml | 3 + .../targets/cs_network_acl/tasks/main.yml | 90 +++++++ 4 files changed, 319 insertions(+) create mode 100644 lib/ansible/modules/cloud/cloudstack/cs_network_acl.py create mode 100644 test/integration/targets/cs_network_acl/aliases create mode 100644 test/integration/targets/cs_network_acl/meta/main.yml create mode 100644 test/integration/targets/cs_network_acl/tasks/main.yml diff --git a/lib/ansible/modules/cloud/cloudstack/cs_network_acl.py b/lib/ansible/modules/cloud/cloudstack/cs_network_acl.py new file mode 100644 index 0000000000..52e643dcf8 --- /dev/null +++ b/lib/ansible/modules/cloud/cloudstack/cs_network_acl.py @@ -0,0 +1,224 @@ +#!/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_network_acl +short_description: Manages network access control lists (ACL) on Apache CloudStack based clouds. +description: + - Create and remove network ACLs. +version_added: "2.4" +author: "René Moser (@resmo)" +options: + name: + description: + - Name of the network ACL. + required: true + description: + description: + - Description of the network ACL. + - If not set, identical to C(name). + required: false + default: null + vpc: + description: + - VPC the network ACL is related to. + required: true + state: + description: + - State of the network ACL. + required: false + default: 'present' + choices: [ 'present', 'absent' ] + domain: + description: + - Domain the network ACL rule is related to. + required: false + default: null + account: + description: + - Account the network ACL rule is related to. + required: false + default: null + project: + description: + - Name of the project the network ACL 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 = ''' +# create a network ACL +local_action: + module: cs_network_acl + name: Webserver ACL + description: a more detailed description of the ACL + vpc: customers + +# remove a network ACL +local_action: + module: cs_network_acl + name: Webserver ACL + vpc: customers + state: absent +''' + +RETURN = ''' +--- +name: + description: Name of the network ACL. + returned: success + type: string + sample: customer acl +description: + description: Description of the network ACL. + returned: success + type: string + sample: Example description of a network ACL +vpc: + description: VPC of the network ACL. + returned: success + type: string + sample: customer vpc +zone: + description: Zone the VPC is related to. + returned: success + type: string + sample: ch-gva-2 +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.cloudstack import ( + AnsibleCloudStack, + CloudStackException, + cs_argument_spec, + cs_required_together +) + + +class AnsibleCloudStackNetworkAcl(AnsibleCloudStack): + + def __init__(self, module): + super(AnsibleCloudStackNetworkAcl, self).__init__(module) + + def get_network_acl(self): + args = { + 'name': self.module.params.get('name'), + 'vpcid': self.get_vpc(key='id'), + } + network_acls = self.cs.listNetworkACLLists(**args) + if network_acls: + return network_acls['networkacllist'][0] + return None + + def present_network_acl(self): + network_acl = self.get_network_acl() + if not network_acl: + self.result['changed'] = True + args = { + 'name': self.module.params.get('name'), + 'description': self.get_or_fallback('description', 'name'), + 'vpcid': self.get_vpc(key='id') + } + if not self.module.check_mode: + res = self.cs.createNetworkACLList(**args) + if 'errortext' in res: + self.fail_json(msg="Failed: '%s'" % res['errortext']) + + poll_async = self.module.params.get('poll_async') + if poll_async: + network_acl = self.poll_job(res, 'networkacllist') + + return network_acl + + def absent_network_acl(self): + network_acl = self.get_network_acl() + if network_acl: + self.result['changed'] = True + args = { + 'id': network_acl['id'], + } + if not self.module.check_mode: + res = self.cs.deleteNetworkACLList(**args) + if 'errortext' in res: + self.fail_json(msg="Failed: '%s'" % res['errortext']) + + poll_async = self.module.params.get('poll_async') + if poll_async: + self.poll_job(res, 'networkacllist') + + return network_acl + + +def main(): + argument_spec = cs_argument_spec() + argument_spec.update(dict( + name=dict(required=True), + description=dict(), + vpc=dict(required=True), + state=dict(choices=['present', 'absent'], default='present'), + zone=dict(), + domain=dict(), + account=dict(), + project=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_network_acl = AnsibleCloudStackNetworkAcl(module) + + state = module.params.get('state') + if state == 'absent': + network_acl = acs_network_acl.absent_network_acl() + else: + network_acl = acs_network_acl.present_network_acl() + + result = acs_network_acl.get_result(network_acl) + + 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_network_acl/aliases b/test/integration/targets/cs_network_acl/aliases new file mode 100644 index 0000000000..ba249b99d7 --- /dev/null +++ b/test/integration/targets/cs_network_acl/aliases @@ -0,0 +1,2 @@ +cloud/cs +posix/ci/cloud/cs diff --git a/test/integration/targets/cs_network_acl/meta/main.yml b/test/integration/targets/cs_network_acl/meta/main.yml new file mode 100644 index 0000000000..e9a5b9eeae --- /dev/null +++ b/test/integration/targets/cs_network_acl/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - cs_common diff --git a/test/integration/targets/cs_network_acl/tasks/main.yml b/test/integration/targets/cs_network_acl/tasks/main.yml new file mode 100644 index 0000000000..5b39ac379b --- /dev/null +++ b/test/integration/targets/cs_network_acl/tasks/main.yml @@ -0,0 +1,90 @@ +--- +- 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 network acl absent + cs_network_acl: + name: "{{ cs_resource_prefix }}_acl" + vpc: "{{ cs_resource_prefix }}_vpc" + zone: "{{ cs_common_zone_adv }}" + state: absent + register: acl +- name: verify setup network acl absent + assert: + that: + - acl|success + +- name: test fail missing param name and vpc for network acl + cs_network_acl: + ignore_errors: true + register: acl +- name: verify test fail missing param name and vpc for network acl + assert: + that: + - acl|failed + - "acl.msg.startswith('missing required arguments: ')" + +- name: test create network acl + cs_network_acl: + name: "{{ cs_resource_prefix }}_acl" + vpc: "{{ cs_resource_prefix }}_vpc" + zone: "{{ cs_common_zone_adv }}" + register: acl +- name: verify test create network acl + assert: + that: + - acl|success + - acl|changed + - acl.vpc == "{{ cs_resource_prefix }}_vpc" + - acl.name == "{{ cs_resource_prefix }}_acl" + +- name: test create network acl idempotence + cs_network_acl: + name: "{{ cs_resource_prefix }}_acl" + vpc: "{{ cs_resource_prefix }}_vpc" + zone: "{{ cs_common_zone_adv }}" + register: acl +- name: verify test create network acl idempotence + assert: + that: + - acl|success + - not acl|changed + - acl.vpc == "{{ cs_resource_prefix }}_vpc" + - acl.name == "{{ cs_resource_prefix }}_acl" + +- name: test remove network acl + cs_network_acl: + name: "{{ cs_resource_prefix }}_acl" + vpc: "{{ cs_resource_prefix }}_vpc" + zone: "{{ cs_common_zone_adv }}" + state: absent + register: acl +- name: verify test remove network acl + assert: + that: + - acl|success + - acl|changed + - acl.vpc == "{{ cs_resource_prefix }}_vpc" + - acl.name == "{{ cs_resource_prefix }}_acl" + +- name: test remove network acl idempotence + cs_network_acl: + name: "{{ cs_resource_prefix }}_acl" + vpc: "{{ cs_resource_prefix }}_vpc" + zone: "{{ cs_common_zone_adv }}" + state: absent + register: acl +- name: verify test remove network acl idempotence + assert: + that: + - acl|success + - not acl|changed