diff --git a/changelogs/fragments/47846-cs_ip_address-fix-vpc-vs-network.yaml b/changelogs/fragments/47846-cs_ip_address-fix-vpc-vs-network.yaml new file mode 100644 index 00000000000..3a78c270d5d --- /dev/null +++ b/changelogs/fragments/47846-cs_ip_address-fix-vpc-vs-network.yaml @@ -0,0 +1,2 @@ +bugfixes: +- cs_ip_address - fix vpc use case failed if network param provided. Ensured vpc and network are mutually exclusive. diff --git a/lib/ansible/modules/cloud/cloudstack/cs_ip_address.py b/lib/ansible/modules/cloud/cloudstack/cs_ip_address.py index 678d488cbfb..0c3c90e9540 100644 --- a/lib/ansible/modules/cloud/cloudstack/cs_ip_address.py +++ b/lib/ansible/modules/cloud/cloudstack/cs_ip_address.py @@ -27,16 +27,18 @@ options: ip_address: description: - Public IP address. - - Required if C(state=absent) and C(tags) is not set + - Required if I(state=absent) and I(tags) is not set. domain: description: - Domain the IP address is related to. network: description: - Network the IP address is related to. + - Mutually exclusive with I(vpc). vpc: description: - VPC the IP address is related to. + - Mutually exclusive with I(network). version_added: "2.2" account: description: @@ -55,7 +57,7 @@ options: choices: [ present, absent ] tags: description: - - List of tags. Tags are a list of dictionaries having keys C(key) and C(value). + - List of tags. Tags are a list of dictionaries having keys I(key) and I(value). - Tags can be used as an unique identifier for the IP Addresses. - In this case, at least one of them must be unique to ensure idempontency. aliases: [ 'tag' ] @@ -195,7 +197,8 @@ class AnsibleCloudStackIPAddress(AnsibleCloudStack): 'account': self.get_account(key='name'), 'domainid': self.get_domain(key='id'), 'projectid': self.get_project(key='id'), - 'networkid': self.get_network(key='id'), + # For the VPC case networkid is irrelevant, special case and we have to ignore it here. + 'networkid': self.get_network(key='id') if not self.module.params.get('vpc') else None, 'zoneid': self.get_zone(key='id'), 'vpcid': self.get_vpc(key='id'), } @@ -249,6 +252,9 @@ def main(): required_if=[ ('state', 'absent', ['ip_address', 'tags'], True), ], + mutually_exclusive=( + ['vpc', 'network'], + ), supports_check_mode=True ) diff --git a/test/integration/targets/cs_ip_address/tasks/main.yml b/test/integration/targets/cs_ip_address/tasks/main.yml index f6c707e51c2..802647d07a8 100644 --- a/test/integration/targets/cs_ip_address/tasks/main.yml +++ b/test/integration/targets/cs_ip_address/tasks/main.yml @@ -1,212 +1,19 @@ --- -- name: setup ensure the test network is absent - cs_network: - name: ipaddr_test_network - state: absent - zone: "{{ cs_common_zone_adv }}" - -- name: setup create the test network - cs_network: - name: ipaddr_test_network - network_offering: DefaultIsolatedNetworkOfferingWithSourceNatService - state: present - zone: "{{ cs_common_zone_adv }}" - register: base_network -- name: setup verify create the test network - assert: - that: - - base_network is successful - -- name: setup clean ip_address with tags - cs_ip_address: - state: absent - tags: - - key: unique_id - value: "adacd65e-7868-5ebf-9f8b-e6e0ea779861" - network: ipaddr_test_network - zone: "{{ cs_common_zone_adv }}" - -- name: setup associate ip_address for SNAT - cs_ip_address: - network: ipaddr_test_network - zone: "{{ cs_common_zone_adv }}" - register: ip_address_snat - -- name: test associate ip_address in check mode - cs_ip_address: - network: ipaddr_test_network - zone: "{{ cs_common_zone_adv }}" - check_mode: true - register: ip_address -- name: verify test associate ip_address in check mode - assert: - that: - - ip_address is successful - - ip_address is changed - -- name: test associate ip_address - cs_ip_address: - network: ipaddr_test_network - zone: "{{ cs_common_zone_adv }}" - register: ip_address -- name: verify test associate ip_address - assert: - that: - - ip_address is successful - - ip_address is changed - - ip_address.ip_address is defined - -- name: test associate ip_address with tags in check mode - cs_ip_address: - network: ipaddr_test_network - tags: - - key: unique_id - value: "adacd65e-7868-5ebf-9f8b-e6e0ea779861" - zone: "{{ cs_common_zone_adv }}" - register: ip_address_tag - check_mode: true -- name: verify test associate ip_address with tags in check mode - assert: - that: - - ip_address_tag is successful - - ip_address_tag is changed - -- name: test associate ip_address with tags - cs_ip_address: - network: ipaddr_test_network - tags: - - key: unique_id - value: "adacd65e-7868-5ebf-9f8b-e6e0ea779861" - zone: "{{ cs_common_zone_adv }}" - register: ip_address_tag -- name: verify test associate ip_address with tags - assert: - that: - - ip_address_tag is successful - - ip_address_tag is changed - - ip_address_tag.ip_address is defined - - ip_address_tag.tags.0.key == "unique_id" - - ip_address_tag.tags.0.value == "adacd65e-7868-5ebf-9f8b-e6e0ea779861" - -- name: test associate ip_address with tags idempotence - cs_ip_address: - network: ipaddr_test_network - tags: - - key: unique_id - value: "adacd65e-7868-5ebf-9f8b-e6e0ea779861" - zone: "{{ cs_common_zone_adv }}" - register: ip_address_tag -- name: verify test associate ip_address with tags idempotence - assert: - that: - - ip_address_tag is successful - - ip_address_tag is not changed - - ip_address_tag.ip_address is defined - - ip_address_tag.state == "Allocated" - - ip_address_tag.tags.0.key == "unique_id" - - ip_address_tag.tags.0.value == "adacd65e-7868-5ebf-9f8b-e6e0ea779861" - -- name: test disassiociate ip_address with missing param ip_address - cs_ip_address: - state: absent - network: ipaddr_test_network - zone: "{{ cs_common_zone_adv }}" - ignore_errors: true - register: ip_address_err -- name: verify test disassiociate ip_address with missing param ip_address - assert: - that: - - ip_address_err is failed - - 'ip_address_err.msg == "state is absent but any of the following are missing: ip_address, tags"' - -- name: test disassociate ip_address in check mode +- name: test fail vpc and network mutually exclusive cs_ip_address: - state: absent - ip_address: "{{ ip_address.ip_address }}" - network: ipaddr_test_network + network: "{{ cs_resource_prefix }}_net_vpc" + vpc: "foobar" zone: "{{ cs_common_zone_adv }}" - check_mode: true + ignore_errors: yes register: ip_address -- name: verify test disassociate ip_address in check mode +- name: verify test fail vpc and network mutually exclusive assert: that: - - ip_address is successful - - ip_address is changed + - ip_address is failed + - 'ip_address.msg == "parameters are mutually exclusive: vpc, network"' -- name: test disassociate ip_address - cs_ip_address: - state: absent - ip_address: "{{ ip_address.ip_address }}" - network: ipaddr_test_network - zone: "{{ cs_common_zone_adv }}" - register: ip_address -- name: verify test disassociate ip_address - assert: - that: - - ip_address is successful - - ip_address is changed +- name: run test for network setup + import_tasks: network.yml -- name: test disassociate ip_address idempotence - cs_ip_address: - state: absent - ip_address: "{{ ip_address.ip_address }}" - network: ipaddr_test_network - zone: "{{ cs_common_zone_adv }}" - register: ip_address -- name: verify test disassociate ip_address idempotence - assert: - that: - - ip_address is successful - - ip_address is not changed - -- name: test disassociate ip_address with tags with check mode - cs_ip_address: - state: absent - tags: - - key: unique_id - value: "adacd65e-7868-5ebf-9f8b-e6e0ea779861" - network: ipaddr_test_network - zone: "{{ cs_common_zone_adv }}" - check_mode: true - register: ip_address -- name: verify test disassociate ip_address with tags in check mode - assert: - that: - - ip_address is successful - - ip_address is changed - -- name: test disassociate ip_address with tags - cs_ip_address: - state: absent - tags: - - key: unique_id - value: "adacd65e-7868-5ebf-9f8b-e6e0ea779861" - network: ipaddr_test_network - zone: "{{ cs_common_zone_adv }}" - register: ip_address -- name: verify test disassociate ip_address with tags - assert: - that: - - ip_address is successful - - ip_address is changed - -- name: test disassociate ip_address with tags idempotence - cs_ip_address: - state: absent - tags: - - key: unique_id - value: "adacd65e-7868-5ebf-9f8b-e6e0ea779861" - network: ipaddr_test_network - zone: "{{ cs_common_zone_adv }}" - register: ip_address -- name: verify test disassociate ip_address with tags idempotence - assert: - that: - - ip_address is successful - - ip_address is not changed - -- name: clean the test network - cs_network: - name: ipaddr_test_network - state: absent - zone: "{{ cs_common_zone_adv }}" +- name: run test for vpc setup + import_tasks: vpc.yml diff --git a/test/integration/targets/cs_ip_address/tasks/network.yml b/test/integration/targets/cs_ip_address/tasks/network.yml new file mode 100644 index 00000000000..f6c707e51c2 --- /dev/null +++ b/test/integration/targets/cs_ip_address/tasks/network.yml @@ -0,0 +1,212 @@ +--- +- name: setup ensure the test network is absent + cs_network: + name: ipaddr_test_network + state: absent + zone: "{{ cs_common_zone_adv }}" + +- name: setup create the test network + cs_network: + name: ipaddr_test_network + network_offering: DefaultIsolatedNetworkOfferingWithSourceNatService + state: present + zone: "{{ cs_common_zone_adv }}" + register: base_network +- name: setup verify create the test network + assert: + that: + - base_network is successful + +- name: setup clean ip_address with tags + cs_ip_address: + state: absent + tags: + - key: unique_id + value: "adacd65e-7868-5ebf-9f8b-e6e0ea779861" + network: ipaddr_test_network + zone: "{{ cs_common_zone_adv }}" + +- name: setup associate ip_address for SNAT + cs_ip_address: + network: ipaddr_test_network + zone: "{{ cs_common_zone_adv }}" + register: ip_address_snat + +- name: test associate ip_address in check mode + cs_ip_address: + network: ipaddr_test_network + zone: "{{ cs_common_zone_adv }}" + check_mode: true + register: ip_address +- name: verify test associate ip_address in check mode + assert: + that: + - ip_address is successful + - ip_address is changed + +- name: test associate ip_address + cs_ip_address: + network: ipaddr_test_network + zone: "{{ cs_common_zone_adv }}" + register: ip_address +- name: verify test associate ip_address + assert: + that: + - ip_address is successful + - ip_address is changed + - ip_address.ip_address is defined + +- name: test associate ip_address with tags in check mode + cs_ip_address: + network: ipaddr_test_network + tags: + - key: unique_id + value: "adacd65e-7868-5ebf-9f8b-e6e0ea779861" + zone: "{{ cs_common_zone_adv }}" + register: ip_address_tag + check_mode: true +- name: verify test associate ip_address with tags in check mode + assert: + that: + - ip_address_tag is successful + - ip_address_tag is changed + +- name: test associate ip_address with tags + cs_ip_address: + network: ipaddr_test_network + tags: + - key: unique_id + value: "adacd65e-7868-5ebf-9f8b-e6e0ea779861" + zone: "{{ cs_common_zone_adv }}" + register: ip_address_tag +- name: verify test associate ip_address with tags + assert: + that: + - ip_address_tag is successful + - ip_address_tag is changed + - ip_address_tag.ip_address is defined + - ip_address_tag.tags.0.key == "unique_id" + - ip_address_tag.tags.0.value == "adacd65e-7868-5ebf-9f8b-e6e0ea779861" + +- name: test associate ip_address with tags idempotence + cs_ip_address: + network: ipaddr_test_network + tags: + - key: unique_id + value: "adacd65e-7868-5ebf-9f8b-e6e0ea779861" + zone: "{{ cs_common_zone_adv }}" + register: ip_address_tag +- name: verify test associate ip_address with tags idempotence + assert: + that: + - ip_address_tag is successful + - ip_address_tag is not changed + - ip_address_tag.ip_address is defined + - ip_address_tag.state == "Allocated" + - ip_address_tag.tags.0.key == "unique_id" + - ip_address_tag.tags.0.value == "adacd65e-7868-5ebf-9f8b-e6e0ea779861" + +- name: test disassiociate ip_address with missing param ip_address + cs_ip_address: + state: absent + network: ipaddr_test_network + zone: "{{ cs_common_zone_adv }}" + ignore_errors: true + register: ip_address_err +- name: verify test disassiociate ip_address with missing param ip_address + assert: + that: + - ip_address_err is failed + - 'ip_address_err.msg == "state is absent but any of the following are missing: ip_address, tags"' + +- name: test disassociate ip_address in check mode + cs_ip_address: + state: absent + ip_address: "{{ ip_address.ip_address }}" + network: ipaddr_test_network + zone: "{{ cs_common_zone_adv }}" + check_mode: true + register: ip_address +- name: verify test disassociate ip_address in check mode + assert: + that: + - ip_address is successful + - ip_address is changed + +- name: test disassociate ip_address + cs_ip_address: + state: absent + ip_address: "{{ ip_address.ip_address }}" + network: ipaddr_test_network + zone: "{{ cs_common_zone_adv }}" + register: ip_address +- name: verify test disassociate ip_address + assert: + that: + - ip_address is successful + - ip_address is changed + +- name: test disassociate ip_address idempotence + cs_ip_address: + state: absent + ip_address: "{{ ip_address.ip_address }}" + network: ipaddr_test_network + zone: "{{ cs_common_zone_adv }}" + register: ip_address +- name: verify test disassociate ip_address idempotence + assert: + that: + - ip_address is successful + - ip_address is not changed + +- name: test disassociate ip_address with tags with check mode + cs_ip_address: + state: absent + tags: + - key: unique_id + value: "adacd65e-7868-5ebf-9f8b-e6e0ea779861" + network: ipaddr_test_network + zone: "{{ cs_common_zone_adv }}" + check_mode: true + register: ip_address +- name: verify test disassociate ip_address with tags in check mode + assert: + that: + - ip_address is successful + - ip_address is changed + +- name: test disassociate ip_address with tags + cs_ip_address: + state: absent + tags: + - key: unique_id + value: "adacd65e-7868-5ebf-9f8b-e6e0ea779861" + network: ipaddr_test_network + zone: "{{ cs_common_zone_adv }}" + register: ip_address +- name: verify test disassociate ip_address with tags + assert: + that: + - ip_address is successful + - ip_address is changed + +- name: test disassociate ip_address with tags idempotence + cs_ip_address: + state: absent + tags: + - key: unique_id + value: "adacd65e-7868-5ebf-9f8b-e6e0ea779861" + network: ipaddr_test_network + zone: "{{ cs_common_zone_adv }}" + register: ip_address +- name: verify test disassociate ip_address with tags idempotence + assert: + that: + - ip_address is successful + - ip_address is not changed + +- name: clean the test network + cs_network: + name: ipaddr_test_network + state: absent + zone: "{{ cs_common_zone_adv }}" diff --git a/test/integration/targets/cs_ip_address/tasks/vpc.yml b/test/integration/targets/cs_ip_address/tasks/vpc.yml new file mode 100644 index 00000000000..3a089ebd3e2 --- /dev/null +++ b/test/integration/targets/cs_ip_address/tasks/vpc.yml @@ -0,0 +1,121 @@ +--- +- name: setup vpc + cs_vpc: + name: "{{ cs_resource_prefix }}_vpc_ip_address" + cidr: 10.10.111.0/16 + zone: "{{ cs_common_zone_adv }}" + register: vpc +- name: verify setup vpc + assert: + that: + - vpc is successful + +- name: setup clean ip_address with tags + cs_ip_address: + state: absent + vpc: "{{ cs_resource_prefix }}_vpc_ip_address" + tags: + - key: unique_id + value: "86cdce4c-dce7-11e8-8394-00262df3bf70" + zone: "{{ cs_common_zone_adv }}" + +- name: test associate ip_address in vpc with tags in check mode + cs_ip_address: + vpc: "{{ cs_resource_prefix }}_vpc_ip_address" + tags: + - key: unique_id + value: "86cdce4c-dce7-11e8-8394-00262df3bf70" + zone: "{{ cs_common_zone_adv }}" + register: ip_address_tag + check_mode: yes +- name: verify test associate ip_address in vpc with tags in check mode + assert: + that: + - ip_address_tag is successful + - ip_address_tag is changed + +- name: test associate ip_address in vpc with tags + cs_ip_address: + vpc: "{{ cs_resource_prefix }}_vpc_ip_address" + tags: + - key: unique_id + value: "86cdce4c-dce7-11e8-8394-00262df3bf70" + zone: "{{ cs_common_zone_adv }}" + register: ip_address_tag +- name: verify test associate ip_address in vpc with tags + assert: + that: + - ip_address_tag is successful + - ip_address_tag is changed + - ip_address_tag.ip_address is defined + - ip_address_tag.tags.0.key == "unique_id" + - ip_address_tag.tags.0.value == "86cdce4c-dce7-11e8-8394-00262df3bf70" + +- name: test associate ip_address in vpc with tags idempotence + cs_ip_address: + vpc: "{{ cs_resource_prefix }}_vpc_ip_address" + tags: + - key: unique_id + value: "86cdce4c-dce7-11e8-8394-00262df3bf70" + zone: "{{ cs_common_zone_adv }}" + register: ip_address_tag +- name: verify test associate ip_address in vpc with tags idempotence + assert: + that: + - ip_address_tag is successful + - ip_address_tag is not changed + - ip_address_tag.ip_address is defined + - ip_address_tag.state == "Allocated" + - ip_address_tag.tags.0.key == "unique_id" + - ip_address_tag.tags.0.value == "86cdce4c-dce7-11e8-8394-00262df3bf70" + +- name: test disassociate ip_address in vpc in check mode + cs_ip_address: + state: absent + ip_address: "{{ ip_address_tag.ip_address }}" + vpc: "{{ cs_resource_prefix }}_vpc_ip_address" + zone: "{{ cs_common_zone_adv }}" + check_mode: true + register: ip_address +- name: verify test disassociate ip_address in vpc in check mode + assert: + that: + - ip_address is successful + - ip_address is changed + +- name: test disassociate ip_address in vpc + cs_ip_address: + state: absent + ip_address: "{{ ip_address_tag.ip_address }}" + vpc: "{{ cs_resource_prefix }}_vpc_ip_address" + zone: "{{ cs_common_zone_adv }}" + register: ip_address +- name: verify test disassociate ip_address in vpc + assert: + that: + - ip_address is successful + - ip_address is changed + +- name: test disassociate ip_address in vpc idempotence + cs_ip_address: + state: absent + ip_address: "{{ ip_address_tag.ip_address }}" + vpc: "{{ cs_resource_prefix }}_vpc_ip_address" + zone: "{{ cs_common_zone_adv }}" + register: ip_address +- name: verify test disassociate ip_address in vpc idempotence + assert: + that: + - ip_address is successful + - ip_address is not changed + +- name: cleanup vpc + cs_vpc: + name: "{{ cs_resource_prefix }}_vpc_ip_address" + zone: "{{ cs_common_zone_adv }}" + state: absent + register: vpc +- name: verify cleanup vpc + assert: + that: + - vpc is successful diff --git a/test/integration/targets/cs_vpc/tasks/main.yml b/test/integration/targets/cs_vpc/tasks/main.yml index 54e7eb1e19c..11e310a5f56 100644 --- a/test/integration/targets/cs_vpc/tasks/main.yml +++ b/test/integration/targets/cs_vpc/tasks/main.yml @@ -380,7 +380,6 @@ - name: test get ip address in vpc cs_ip_address: - network: "{{ cs_resource_prefix }}_net_vpc" vpc: "{{ cs_resource_prefix }}_vpc" zone: "{{ cs_common_zone_adv }}" register: ip_address @@ -578,7 +577,6 @@ - name: test remove ip address from vpc cs_ip_address: - network: "{{ cs_resource_prefix }}_net_vpc" vpc: "{{ cs_resource_prefix }}_vpc" zone: "{{ cs_common_zone_adv }}" ip_address: "{{ ip_address.ip_address }}"