diff --git a/CHANGELOG.md b/CHANGELOG.md index 9198a86764..7fe01dad26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -169,6 +169,7 @@ Ansible Changes By Release * ce_vxlan_vap - cloudstack * cs_network_acl + * cs_network_acl_rule * cs_vpn_gateway - crypto * openssl_csr diff --git a/lib/ansible/module_utils/cloudstack.py b/lib/ansible/module_utils/cloudstack.py index 4ed9ab04b2..f65197ffd2 100644 --- a/lib/ansible/module_utils/cloudstack.py +++ b/lib/ansible/module_utils/cloudstack.py @@ -130,6 +130,7 @@ class AnsibleCloudStack(object): self.os_type = None self.hypervisor = None self.capabilities = None + self.network_acl = None def _connect(self): api_region = self.module.params.get('api_region') or os.environ.get('CLOUDSTACK_REGION') @@ -224,6 +225,21 @@ class AnsibleCloudStack(object): self.fail_json(msg="Something went wrong: %s not found" % key) return my_dict + def get_network_acl(self, key=None): + if self.network_acl is None: + args = { + 'name': self.module.params.get('network_acl'), + 'vpcid': self.get_vpc(key='id'), + } + network_acls = self.cs.listNetworkACLLists(**args) + if network_acls: + self.network_acl = network_acls['networkacllist'][0] + self.result['network_acl'] = self.network_acl['name'] + if self.network_acl: + return self._get_by_key(key, self.network_acl) + else: + self.fail_json("Network ACL %s not found" % self.module.params.get('network_acl')) + def get_vpc(self, key=None): """Return a VPC dictionary or the value of given key of.""" if self.vpc: diff --git a/lib/ansible/modules/cloud/cloudstack/cs_network_acl_rule.py b/lib/ansible/modules/cloud/cloudstack/cs_network_acl_rule.py new file mode 100644 index 0000000000..50dd29d478 --- /dev/null +++ b/lib/ansible/modules/cloud/cloudstack/cs_network_acl_rule.py @@ -0,0 +1,491 @@ +#!/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_rule +short_description: Manages network access control list (ACL) rules on Apache CloudStack based clouds. +description: + - Add, update and remove network ACL rules. +version_added: "2.4" +author: "René Moser (@resmo)" +options: + network_acl: + description: + - Name of the network ACL. + required: true + aliases: [ acl ] + cidr: + description: + - CIDR of the rule. + required: false + default: '0.0.0.0/0' + rule_position: + description: + - CIDR of the rule. + required: true + aliases: [ number ] + protocol: + description: + - Protocol of the rule + choices: [ tcp, udp, icmp, all, by_number ] + required: false + default: tcp + protocol_number: + description: + - Protocol number from 1 to 256 required if C(protocol=by_number). + required: false + default: null + start_port: + description: + - Start port for this rule. + - Considered if C(protocol=tcp) or C(protocol=udp). + required: false + default: null + aliases: [ port ] + end_port: + description: + - End port for this rule. + - Considered if C(protocol=tcp) or C(protocol=udp). + - If not specified, equal C(start_port). + required: false + default: null + icmp_type: + description: + - Type of the icmp message being sent. + - Considered if C(protocol=icmp). + required: false + default: null + icmp_code: + description: + - Error code for this icmp message. + - Considered if C(protocol=icmp). + required: false + default: null + vpc: + description: + - VPC the network ACL is related to. + required: true + traffic_type: + description: + - Traffic type of the rule. + required: false + choices: [ ingress, egress ] + default: ingress + aliases: [ type ] + action_policy: + description: + - Action policy of the rule. + required: false + choices: [ allow, deny ] + default: ingress + aliases: [ action ] + tags: + description: + - List of tags. Tags are a list of dictionaries having keys C(key) and C(value). + - "If you want to delete all tags, set a empty list e.g. C(tags: [])." + required: false + default: null + aliases: [ tag ] + domain: + description: + - Domain the VPC is related to. + required: false + default: null + account: + description: + - Account the VPC is related to. + required: false + default: null + project: + description: + - Name of the project the VPC is related to. + required: false + default: null + zone: + description: + - Name of the zone the VPC related to. + - If not set, default zone is used. + required: false + default: null + state: + description: + - State of the network ACL rule. + required: false + default: present + choices: [ present, absent ] + poll_async: + description: + - Poll async jobs until job has finished. + required: false + default: true +extends_documentation_fragment: cloudstack +''' + +EXAMPLES = ''' +# create a network ACL rule, allow port 80 ingress +local_action: + module: cs_network_acl_rule + network_acl: web + rule_position: 1 + vpc: my vpc + traffic_type: ingress + action_policy: allow + port: 80 + cidr: 0.0.0.0/0 + +# create a network ACL rule, deny port range 8000-9000 ingress for 10.20.0.0/16 +local_action: + module: cs_network_acl_rule + network_acl: web + rule_position: 1 + vpc: my vpc + traffic_type: ingress + action_policy: deny + start_port: 8000 + end_port: 8000 + cidr: 10.20.0.0/16 + +# create a network ACL rule +local_action: + module: cs_network_acl_rule + network_acl: web + rule_position: 1 + vpc: my vpc + traffic_type: ingress + action_policy: deny + start_port: 8000 + end_port: 8000 + cidr: 10.20.0.0/16 + +# remove a network ACL rule +local_action: + module: cs_network_acl_rule + network_acl: web + rule_position: 1 + vpc: my vpc + state: absent +''' + +RETURN = ''' +--- +network_acl: + description: Name of the network ACL. + returned: success + type: string + sample: customer acl +cidr: + description: CIDR of the network ACL rule. + returned: success + type: string + sample: 0.0.0.0/0 +rule_position: + description: Position of the network ACL rule. + returned: success + type: int + sample: 1 +action_policy: + description: Action policy of the network ACL rule. + returned: success + type: string + sample: deny +traffic_type: + description: Traffic type of the network ACL rule. + returned: success + type: string + sample: ingress +protocol: + description: Protocol of the network ACL rule. + returned: success + type: string + sample: tcp +protocol_number: + description: Protocol number in case protocol is by number. + returned: success + type: int + sample: 8 +start_port: + description: Start port of the network ACL rule. + returned: success + type: int + sample: 80 +end_port: + description: End port of the network ACL rule. + returned: success + type: int + sample: 80 +icmp_code: + description: ICMP code of the network ACL rule. + returned: success + type: int + sample: 8 +icmp_type: + description: ICMP type of the network ACL rule. + returned: success + type: int + sample: 0 +state: + description: State of the network ACL rule. + returned: success + type: string + sample: Active +vpc: + description: VPC of the network ACL. + returned: success + type: string + sample: customer vpc +tags: + description: List of resource tags associated with the network ACL rule. + returned: success + type: dict + sample: '[ { "key": "foo", "value": "bar" } ]' +domain: + description: Domain the network ACL rule is related to. + returned: success + type: string + sample: example domain +account: + description: Account the network ACL rule is related to. + returned: success + type: string + sample: example account +project: + description: Name of project the network ACL rule is related to. + returned: success + type: string + sample: Production +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 AnsibleCloudStackNetworkAclRule(AnsibleCloudStack): + + def __init__(self, module): + super(AnsibleCloudStackNetworkAclRule, self).__init__(module) + self.returns = { + 'cidrlist': 'cidr', + 'action': 'action_policy', + 'protocol': 'protocol', + 'icmpcode': 'icmp_code', + 'icmptype': 'icmp_type', + 'number': 'rule_position', + 'traffictype': 'traffic_type', + } + # these values will be casted to int + self.returns_to_int = { + 'startport': 'start_port', + 'endport': 'end_port', + } + + def get_network_acl_rule(self): + args = { + 'aclid': self.get_network_acl(key='id'), + 'account': self.get_account(key='name'), + 'domainid': self.get_domain(key='id'), + 'projectid': self.get_project(key='id'), + } + network_acl_rules = self.cs.listNetworkACLs(**args) + for acl_rule in network_acl_rules.get('networkacl', []): + if acl_rule['number'] == self.module.params.get('rule_position'): + return acl_rule + return None + + def present_network_acl_rule(self): + network_acl_rule = self.get_network_acl_rule() + + protocol = self.module.params.get('protocol') + start_port = self.module.params.get('start_port') + end_port = self.get_or_fallback('end_port', 'start_port') + icmp_type = self.module.params.get('icmp_type') + icmp_code = self.module.params.get('icmp_code') + + if protocol in ['tcp', 'udp'] and (start_port is None or end_port is None): + self.module.fail_json(msg="protocol is %s but the following are missing: start_port, end_port" % protocol) + + elif protocol == 'icmp' and (icmp_type is None or icmp_code is None): + self.module.fail_json(msg="protocol is icmp but the following are missing: icmp_type, icmp_code") + + elif protocol == 'by_number' and self.module.params.get('protocol_number') is None: + self.module.fail_json(msg="protocol is by_number but the following are missing: protocol_number") + + if not network_acl_rule: + network_acl_rule = self._create_network_acl_rule(network_acl_rule) + else: + network_acl_rule = self._update_network_acl_rule(network_acl_rule) + + if network_acl_rule: + network_acl_rule = self.ensure_tags(resource=network_acl_rule, resource_type='NetworkACL') + return network_acl_rule + + def absent_network_acl_rule(self): + network_acl_rule = self.get_network_acl_rule() + if network_acl_rule: + self.result['changed'] = True + args = { + 'id': network_acl_rule['id'], + } + if not self.module.check_mode: + res = self.cs.deleteNetworkACL(**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, 'networkacl') + + return network_acl_rule + + def _create_network_acl_rule(self, network_acl_rule): + self.result['changed'] = True + protocol = self.module.params.get('protocol') + args = { + 'aclid': self.get_network_acl(key='id'), + 'action': self.module.params.get('action_policy'), + 'protocol': protocol if protocol != 'by_number' else self.module.params.get('protocol_number'), + 'startport': self.module.params.get('start_port'), + 'endport': self.get_or_fallback('end_port', 'start_port'), + 'number': self.module.params.get('rule_position'), + 'icmpcode': self.module.params.get('icmp_code'), + 'icmptype': self.module.params.get('icmp_type'), + 'traffictype': self.module.params.get('traffic_type'), + 'cidrlist': self.module.params.get('cidr'), + } + if not self.module.check_mode: + res = self.cs.createNetworkACL(**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_rule = self.poll_job(res, 'networkacl') + + return network_acl_rule + + def _update_network_acl_rule(self, network_acl_rule): + protocol = self.module.params.get('protocol') + args = { + 'id': network_acl_rule['id'], + 'action': self.module.params.get('action_policy'), + 'protocol': protocol if protocol != 'by_number' else str(self.module.params.get('protocol_number')), + 'startport': self.module.params.get('start_port'), + 'endport': self.get_or_fallback('end_port', 'start_port'), + 'icmpcode': self.module.params.get('icmp_code'), + 'icmptype': self.module.params.get('icmp_type'), + 'traffictype': self.module.params.get('traffic_type'), + 'cidrlist': self.module.params.get('cidr'), + } + if self.has_changed(args, network_acl_rule): + self.result['changed'] = True + if not self.module.check_mode: + res = self.cs.updateNetworkACLItem(**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_rule = self.poll_job(res, 'networkacl') + + return network_acl_rule + + def get_result(self, network_acl_rule): + super(AnsibleCloudStackNetworkAclRule, self).get_result(network_acl_rule) + if network_acl_rule: + if network_acl_rule['protocol'] not in ['tcp', 'udp', 'icmp', 'all']: + self.result['protocol_number'] = int(network_acl_rule['protocol']) + self.result['protocol'] = 'by_number' + self.result['action_policy'] = self.result['action_policy'].lower() + self.result['traffic_type'] = self.result['traffic_type'].lower() + return self.result + + +def main(): + argument_spec = cs_argument_spec() + argument_spec.update(dict( + network_acl=dict(required=True, aliases=['acl']), + rule_position=dict(required=True, type='int', aliases=['number']), + vpc=dict(required=True), + cidr=dict(default='0.0.0.0/0'), + protocol=dict(choices=['tcp', 'udp', 'icmp', 'all', 'by_number'], default='tcp'), + protocol_number=dict(type='int', choices=list(range(0, 256))), + traffic_type=dict(choices=['ingress', 'egress'], aliases=['type'], default='ingress'), + action_policy=dict(choices=['allow', 'deny'], aliases=['action'], default='allow'), + icmp_type=dict(type='int'), + icmp_code=dict(type='int'), + start_port=dict(type='int', aliases=['port']), + end_port=dict(type='int'), + state=dict(choices=['present', 'absent'], default='present'), + zone=dict(), + domain=dict(), + account=dict(), + project=dict(), + tags=dict(type='list', aliases=['tag']), + poll_async=dict(type='bool', default=True), + )) + + required_together = cs_required_together() + required_together.extend([ + ['icmp_type', 'icmp_code'], + ]) + + module = AnsibleModule( + argument_spec=argument_spec, + required_together=cs_required_together(), + mutually_exclusive=( + ['icmp_type', 'start_port'], + ['icmp_type', 'end_port'], + ), + supports_check_mode=True + ) + + try: + acs_network_acl_rule = AnsibleCloudStackNetworkAclRule(module) + + state = module.params.get('state') + if state == 'absent': + network_acl_rule = acs_network_acl_rule.absent_network_acl_rule() + else: + network_acl_rule = acs_network_acl_rule.present_network_acl_rule() + + result = acs_network_acl_rule.get_result(network_acl_rule) + + 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_rule/aliases b/test/integration/targets/cs_network_acl_rule/aliases new file mode 100644 index 0000000000..ba249b99d7 --- /dev/null +++ b/test/integration/targets/cs_network_acl_rule/aliases @@ -0,0 +1,2 @@ +cloud/cs +posix/ci/cloud/cs diff --git a/test/integration/targets/cs_network_acl_rule/meta/main.yml b/test/integration/targets/cs_network_acl_rule/meta/main.yml new file mode 100644 index 0000000000..e9a5b9eeae --- /dev/null +++ b/test/integration/targets/cs_network_acl_rule/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - cs_common diff --git a/test/integration/targets/cs_network_acl_rule/tasks/main.yml b/test/integration/targets/cs_network_acl_rule/tasks/main.yml new file mode 100644 index 0000000000..3fb6c96f99 --- /dev/null +++ b/test/integration/targets/cs_network_acl_rule/tasks/main.yml @@ -0,0 +1,539 @@ +--- +- 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 + cs_network_acl: + name: "{{ cs_resource_prefix }}_acl" + vpc: "{{ cs_resource_prefix }}_vpc" + zone: "{{ cs_common_zone_adv }}" + register: acl +- name: verify setup network acl + assert: + that: + - acl|success + +- name: setup network acl rule + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 1 + vpc: "{{ cs_resource_prefix }}_vpc" + zone: "{{ cs_common_zone_adv }}" + state: absent + register: acl_rule +- name: verify setup network acl rule + assert: + that: + - acl_rule|success + +- name: test fail missing params + cs_network_acl_rule: + ignore_errors: true + register: acl_rule +- name: verify test fail missing param + assert: + that: + - acl_rule|failed + - "acl_rule.msg.startswith('missing required arguments: ')" + +- name: test fail missing params for tcp + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 1 + vpc: "{{ cs_resource_prefix }}_vpc" + traffic_type: ingress + action_policy: allow + cidr: 0.0.0.0/0 + zone: "{{ cs_common_zone_adv }}" + ignore_errors: true + register: acl_rule +- name: verify test fail missing param for tcp + assert: + that: + - acl_rule|failed + - "acl_rule.msg == 'protocol is tcp but the following are missing: start_port, end_port'" + +- name: test fail missing params for icmp + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 1 + vpc: "{{ cs_resource_prefix }}_vpc" + traffic_type: ingress + action_policy: allow + cidr: 0.0.0.0/0 + protocol: icmp + zone: "{{ cs_common_zone_adv }}" + ignore_errors: true + register: acl_rule +- name: verify test fail missing param for icmp + assert: + that: + - acl_rule|failed + - "acl_rule.msg == 'protocol is icmp but the following are missing: icmp_type, icmp_code'" + +- name: test fail missing params for by number + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 1 + vpc: "{{ cs_resource_prefix }}_vpc" + traffic_type: ingress + action_policy: allow + cidr: 0.0.0.0/0 + protocol: by_number + zone: "{{ cs_common_zone_adv }}" + ignore_errors: true + register: acl_rule +- name: verify test fail missing param for by number + assert: + that: + - acl_rule|failed + - "acl_rule.msg == 'protocol is by_number but the following are missing: protocol_number'" + +- name: test create network acl rule in check mode + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 1 + vpc: "{{ cs_resource_prefix }}_vpc" + traffic_type: ingress + action_policy: allow + port: 80 + cidr: 0.0.0.0/0 + zone: "{{ cs_common_zone_adv }}" + register: acl_rule + check_mode: true +- name: verify test create network acl rule in check mode + assert: + that: + - acl_rule|success + - acl_rule|changed + +- name: test create network acl rule + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 1 + vpc: "{{ cs_resource_prefix }}_vpc" + traffic_type: ingress + action_policy: allow + port: 80 + cidr: 0.0.0.0/0 + zone: "{{ cs_common_zone_adv }}" + register: acl_rule +- name: verify test create network acl rule + assert: + that: + - acl_rule|success + - acl_rule|changed + - acl_rule.vpc == "{{ cs_resource_prefix }}_vpc" + - acl_rule.network_acl == "{{ cs_resource_prefix }}_acl" + - acl_rule.start_port == 80 + - acl_rule.end_port == 80 + - acl_rule.action_policy == "allow" + - acl_rule.cidr == "0.0.0.0/0" + - acl_rule.traffic_type == "ingress" + - acl_rule.rule_position == 1 + +- name: test create network acl rule idempotence + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 1 + vpc: "{{ cs_resource_prefix }}_vpc" + traffic_type: ingress + action_policy: allow + port: 80 + cidr: 0.0.0.0/0 + zone: "{{ cs_common_zone_adv }}" + register: acl_rule +- name: verify test create network acl idempotence + assert: + that: + - acl_rule|success + - not acl_rule|changed + - acl_rule.vpc == "{{ cs_resource_prefix }}_vpc" + - acl_rule.network_acl == "{{ cs_resource_prefix }}_acl" + - acl_rule.start_port == 80 + - acl_rule.end_port == 80 + - acl_rule.action_policy == "allow" + - acl_rule.cidr == "0.0.0.0/0" + - acl_rule.traffic_type == "ingress" + - acl_rule.rule_position == 1 + +- name: test change network acl rule in check mode + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 1 + vpc: "{{ cs_resource_prefix }}_vpc" + traffic_type: egress + action_policy: deny + port: 81 + cidr: 0.0.0.0/0 + zone: "{{ cs_common_zone_adv }}" + register: acl_rule + check_mode: true +- name: verify test change network acl rule in check mode + assert: + that: + - acl_rule|success + - acl_rule|changed + - acl_rule.vpc == "{{ cs_resource_prefix }}_vpc" + - acl_rule.network_acl == "{{ cs_resource_prefix }}_acl" + - acl_rule.start_port == 80 + - acl_rule.end_port == 80 + - acl_rule.action_policy == "allow" + - acl_rule.cidr == "0.0.0.0/0" + - acl_rule.traffic_type == "ingress" + - acl_rule.rule_position == 1 + +- name: test change network acl rule + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 1 + vpc: "{{ cs_resource_prefix }}_vpc" + traffic_type: egress + action_policy: deny + port: 81 + protocol: udp + cidr: 0.0.0.0/0 + zone: "{{ cs_common_zone_adv }}" + register: acl_rule +- name: verify test change network acl rule + assert: + that: + - acl_rule|success + - acl_rule|changed + - acl_rule.vpc == "{{ cs_resource_prefix }}_vpc" + - acl_rule.network_acl == "{{ cs_resource_prefix }}_acl" + - acl_rule.start_port == 81 + - acl_rule.end_port == 81 + - acl_rule.action_policy == "deny" + - acl_rule.cidr == "0.0.0.0/0" + - acl_rule.traffic_type == "egress" + - acl_rule.protocol == "udp" + - acl_rule.rule_position == 1 + +- name: test change network acl rule idempotence + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 1 + vpc: "{{ cs_resource_prefix }}_vpc" + traffic_type: egress + action_policy: deny + port: 81 + protocol: udp + cidr: 0.0.0.0/0 + zone: "{{ cs_common_zone_adv }}" + register: acl_rule +- name: verify test change network acl idempotence + assert: + that: + - acl_rule|success + - not acl_rule|changed + - acl_rule.vpc == "{{ cs_resource_prefix }}_vpc" + - acl_rule.network_acl == "{{ cs_resource_prefix }}_acl" + - acl_rule.start_port == 81 + - acl_rule.end_port == 81 + - acl_rule.action_policy == "deny" + - acl_rule.cidr == "0.0.0.0/0" + - acl_rule.traffic_type == "egress" + - acl_rule.protocol == "udp" + - acl_rule.rule_position == 1 + +- name: test change network acl by protocol number in check mode + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 1 + vpc: "{{ cs_resource_prefix }}_vpc" + traffic_type: egress + action_policy: deny + protocol: by_number + protocol_number: 8 + port: 81 + cidr: 0.0.0.0/0 + zone: "{{ cs_common_zone_adv }}" + register: acl_rule + check_mode: true +- name: verify test change network acl by protocol number in check mode + assert: + that: + - acl_rule|success + - acl_rule|changed + - acl_rule.vpc == "{{ cs_resource_prefix }}_vpc" + - acl_rule.network_acl == "{{ cs_resource_prefix }}_acl" + - acl_rule.start_port == 81 + - acl_rule.end_port == 81 + - acl_rule.action_policy == "deny" + - acl_rule.cidr == "0.0.0.0/0" + - acl_rule.traffic_type == "egress" + - acl_rule.protocol == "udp" + - acl_rule.rule_position == 1 + +- name: test change network acl by protocol number + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 1 + vpc: "{{ cs_resource_prefix }}_vpc" + traffic_type: egress + action_policy: deny + protocol: by_number + protocol_number: 8 + port: 81 + cidr: 0.0.0.0/0 + zone: "{{ cs_common_zone_adv }}" + register: acl_rule +- name: verify test change network acl by protocol number + assert: + that: + - acl_rule|success + - acl_rule|changed + - acl_rule.vpc == "{{ cs_resource_prefix }}_vpc" + - acl_rule.network_acl == "{{ cs_resource_prefix }}_acl" + - acl_rule.start_port == 81 + - acl_rule.end_port == 81 + - acl_rule.action_policy == "deny" + - acl_rule.cidr == "0.0.0.0/0" + - acl_rule.traffic_type == "egress" + - acl_rule.protocol == "by_number" + - acl_rule.protocol_number == 8 + - acl_rule.rule_position == 1 + +- name: test change network acl by protocol number idempotence + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 1 + vpc: "{{ cs_resource_prefix }}_vpc" + traffic_type: egress + action_policy: deny + protocol: by_number + protocol_number: 8 + port: 81 + cidr: 0.0.0.0/0 + zone: "{{ cs_common_zone_adv }}" + register: acl_rule +- name: verify test change network acl by protocol number idempotence + assert: + that: + - acl_rule|success + - not acl_rule|changed + - acl_rule.vpc == "{{ cs_resource_prefix }}_vpc" + - acl_rule.network_acl == "{{ cs_resource_prefix }}_acl" + - acl_rule.start_port == 81 + - acl_rule.end_port == 81 + - acl_rule.action_policy == "deny" + - acl_rule.cidr == "0.0.0.0/0" + - acl_rule.traffic_type == "egress" + - acl_rule.protocol == "by_number" + - acl_rule.protocol_number == 8 + - acl_rule.rule_position == 1 + + +- name: test create 2nd network acl rule in check mode + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 2 + vpc: "{{ cs_resource_prefix }}_vpc" + traffic_type: egress + action_policy: allow + cidr: 10.23.12.0/24 + zone: "{{ cs_common_zone_adv }}" + protocol: all + register: acl_rule + check_mode: true +- name: verify test create 2nd network acl rule in check mode + assert: + that: + - acl_rule|success + - acl_rule|changed + +- name: test create 2nd network acl rule + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 2 + vpc: "{{ cs_resource_prefix }}_vpc" + traffic_type: egress + action_policy: allow + cidr: 10.23.12.0/24 + zone: "{{ cs_common_zone_adv }}" + protocol: all + register: acl_rule +- name: verify test create 2nd network acl rule + assert: + that: + - acl_rule|success + - acl_rule|changed + - acl_rule.vpc == "{{ cs_resource_prefix }}_vpc" + - acl_rule.network_acl == "{{ cs_resource_prefix }}_acl" + - acl_rule.action_policy == "allow" + - acl_rule.cidr == "10.23.12.0/24" + - acl_rule.traffic_type == "egress" + - acl_rule.protocol == "all" + - acl_rule.rule_position == 2 + +- name: test create 2nd network acl rule idempotence + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 2 + vpc: "{{ cs_resource_prefix }}_vpc" + traffic_type: egress + action_policy: allow + cidr: 10.23.12.0/24 + zone: "{{ cs_common_zone_adv }}" + protocol: all + register: acl_rule +- name: verify test create 2nd network acl rule idempotence + assert: + that: + - acl_rule|success + - not acl_rule|changed + - acl_rule.vpc == "{{ cs_resource_prefix }}_vpc" + - acl_rule.network_acl == "{{ cs_resource_prefix }}_acl" + - acl_rule.action_policy == "allow" + - acl_rule.cidr == "10.23.12.0/24" + - acl_rule.traffic_type == "egress" + - acl_rule.protocol == "all" + - acl_rule.rule_position == 2 + +- name: test update 2nd network acl rule to icmp + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 2 + vpc: "{{ cs_resource_prefix }}_vpc" + traffic_type: egress + action_policy: allow + cidr: 10.23.12.0/24 + zone: "{{ cs_common_zone_adv }}" + protocol: icmp + icmp_type: 0 + icmp_code: 8 + register: acl_rule +- name: verify test create 2nd network acl rule + assert: + that: + - acl_rule|success + - acl_rule|changed + - acl_rule.vpc == "{{ cs_resource_prefix }}_vpc" + - acl_rule.network_acl == "{{ cs_resource_prefix }}_acl" + - acl_rule.action_policy == "allow" + - acl_rule.cidr == "10.23.12.0/24" + - acl_rule.traffic_type == "egress" + - acl_rule.protocol == "icmp" + - acl_rule.icmp_type == 0 + - acl_rule.icmp_code == 8 + - acl_rule.rule_position == 2 + +- name: test update 2nd network acl rule to icmp idempotence + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 2 + vpc: "{{ cs_resource_prefix }}_vpc" + traffic_type: egress + action_policy: allow + cidr: 10.23.12.0/24 + zone: "{{ cs_common_zone_adv }}" + protocol: icmp + icmp_type: 0 + icmp_code: 8 + register: acl_rule +- name: verify test create 2nd network acl rule idempotence + assert: + that: + - acl_rule|success + - not acl_rule|changed + - acl_rule.vpc == "{{ cs_resource_prefix }}_vpc" + - acl_rule.network_acl == "{{ cs_resource_prefix }}_acl" + - acl_rule.action_policy == "allow" + - acl_rule.cidr == "10.23.12.0/24" + - acl_rule.traffic_type == "egress" + - acl_rule.protocol == "icmp" + - acl_rule.icmp_type == 0 + - acl_rule.icmp_code == 8 + - acl_rule.rule_position == 2 + +- name: test absent network acl rule in check mode + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 1 + vpc: "{{ cs_resource_prefix }}_vpc" + zone: "{{ cs_common_zone_adv }}" + state: absent + register: acl_rule + check_mode: true +- name: verify test absent network acl rule in check mode + assert: + that: + - acl_rule|success + - acl_rule|changed + - acl_rule.vpc == "{{ cs_resource_prefix }}_vpc" + - acl_rule.network_acl == "{{ cs_resource_prefix }}_acl" + - acl_rule.start_port == 81 + - acl_rule.end_port == 81 + - acl_rule.action_policy == "deny" + - acl_rule.cidr == "0.0.0.0/0" + - acl_rule.traffic_type == "egress" + - acl_rule.rule_position == 1 + +- name: test absent network acl rule + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 1 + vpc: "{{ cs_resource_prefix }}_vpc" + zone: "{{ cs_common_zone_adv }}" + state: absent + register: acl_rule +- name: verify test absent network acl rule + assert: + that: + - acl_rule|success + - acl_rule|changed + - acl_rule.vpc == "{{ cs_resource_prefix }}_vpc" + - acl_rule.network_acl == "{{ cs_resource_prefix }}_acl" + - acl_rule.start_port == 81 + - acl_rule.end_port == 81 + - acl_rule.action_policy == "deny" + - acl_rule.cidr == "0.0.0.0/0" + - acl_rule.traffic_type == "egress" + - acl_rule.rule_position == 1 + +- name: test absent network acl rule idempotence + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 1 + vpc: "{{ cs_resource_prefix }}_vpc" + zone: "{{ cs_common_zone_adv }}" + state: absent + register: acl_rule +- name: verify test absent network acl rule idempotence + assert: + that: + - acl_rule|success + - not acl_rule|changed + +- name: test absent 2nd network acl rule + cs_network_acl_rule: + network_acl: "{{ cs_resource_prefix }}_acl" + rule_position: 2 + vpc: "{{ cs_resource_prefix }}_vpc" + zone: "{{ cs_common_zone_adv }}" + state: absent + register: acl_rule +- name: verify test absent 2nd network acl rule + assert: + that: + - acl_rule|success + - acl_rule|changed + - acl_rule.vpc == "{{ cs_resource_prefix }}_vpc" + - acl_rule.network_acl == "{{ cs_resource_prefix }}_acl" + - acl_rule.action_policy == "allow" + - acl_rule.cidr == "10.23.12.0/24" + - acl_rule.traffic_type == "egress" + - acl_rule.protocol == "icmp" + - acl_rule.icmp_type == 0 + - acl_rule.icmp_code == 8 + - acl_rule.rule_position == 2