--- - name: List all images openstack.cloud.image_info: cloud: "{{ cloud }}" register: images - name: Identify CirrOS image name set_fact: image_name: "{{ images.images|community.general.json_query(query)|first }}" vars: query: "[?starts_with(name, 'cirros')].name" - name: Gather information about public network openstack.cloud.networks_info: cloud: "{{ cloud }}" name: public register: public_network - name: Assert that public network exists assert: that: public_network.networks|length == 1 - name: Create external network openstack.cloud.network: cloud: "{{ cloud }}" state: present name: ansible_external external: true - name: Gather information about external network openstack.cloud.networks_info: cloud: "{{ cloud }}" name: ansible_external register: external_network - name: Create external subnet openstack.cloud.subnet: cloud: "{{ cloud }}" state: present network_name: ansible_external name: ansible_external_subnet cidr: 10.6.6.0/24 - name: Create external port 1 openstack.cloud.port: cloud: "{{ cloud }}" state: present name: ansible_external_port1 network: ansible_external fixed_ips: - ip_address: 10.6.6.50 - name: Create external port 2 openstack.cloud.port: cloud: "{{ cloud }}" state: present name: ansible_external_port2 network: ansible_external fixed_ips: - ip_address: 10.6.6.51 - name: Create internal network openstack.cloud.network: cloud: "{{ cloud }}" state: present name: ansible_internal external: false - name: Create internal subnet openstack.cloud.subnet: cloud: "{{ cloud }}" state: present network_name: ansible_internal name: ansible_internal_subnet cidr: 10.7.7.0/24 - name: Create internal port 1 openstack.cloud.port: cloud: "{{ cloud }}" state: present name: ansible_internal_port1 network: ansible_internal fixed_ips: - ip_address: 10.7.7.100 register: port1 - name: Create internal port 2 openstack.cloud.port: cloud: "{{ cloud }}" state: present name: ansible_internal_port2 network: ansible_internal fixed_ips: - ip_address: 10.7.7.101 register: port2 - name: Create internal port 3 openstack.cloud.port: cloud: "{{ cloud }}" state: present name: ansible_internal_port3 network: ansible_internal fixed_ips: - ip_address: 10.7.7.102 register: port3 - name: Create internal port 4 openstack.cloud.port: cloud: "{{ cloud }}" state: present name: ansible_internal_port4 network: ansible_internal fixed_ips: - ip_address: 10.7.7.103 - ip_address: 10.7.7.104 register: port4 - name: Create router 1 openstack.cloud.router: cloud: "{{ cloud }}" state: present name: ansible_router1 network: ansible_external external_fixed_ips: - subnet: ansible_external_subnet ip: 10.6.6.10 interfaces: - net: ansible_internal subnet: ansible_internal_subnet portip: 10.7.7.1 # Router 2 is required for the simplest, first test that assigns a new floating IP to server # from first available external network or nova pool which is DevStack's public network - name: Create router 2 openstack.cloud.router: cloud: "{{ cloud }}" state: present name: ansible_router2 network: public interfaces: - net: ansible_internal subnet: ansible_internal_subnet portip: 10.7.7.10 - name: Get all floating ips openstack.cloud.floating_ip_info: cloud: "{{ cloud }}" register: fips - name: Check if public network has any floating ips set_fact: public_network_had_fips: "{{ fips.floating_ips| selectattr('floating_network_id', '==', public_network.networks.0.id)| list|length > 0 }}" - name: Create a floating ip on public network (required for simplest, first floating ip test) when: not public_network_had_fips block: - name: Create a floating ip on public network openstack.cloud.floating_ip: cloud: "{{ cloud }}" state: present network: public register: public_fip_result - name: Verify floating ip got created assert: that: - public_fip_result.floating_ip.floating_network_id == public_network.networks.0.id - name: Create a floating ip on public network again openstack.cloud.floating_ip: cloud: "{{ cloud }}" state: present network: public register: public_fip_result - name: Verify idempotency assert: that: public_fip_result is not changed # TODO: Replace with appropriate Ansible module once available - name: Create floating ip 1 on external network command: > openstack --os-cloud={{ cloud }} floating ip create --subnet ansible_external_subnet --floating-ip-address 10.6.6.150 ansible_external when: fips.floating_ips|length == 0 or "10.6.6.150" not in fips.floating_ips|map(attribute="floating_ip_address")|list - name: Create floating ip 2 on external network openstack.cloud.floating_ip: cloud: "{{ cloud }}" state: present network: ansible_external floating_ip_address: 10.6.6.151 register: external_fip2_result - name: Verify floating ip got created assert: that: - external_fip2_result.floating_ip.floating_network_id == external_network.networks.0.id - name: Update floating ip 2 on external network openstack.cloud.floating_ip: cloud: "{{ cloud }}" state: present network: ansible_external floating_ip_address: 10.6.6.151 nat_destination: ansible_internal fixed_address: 10.7.7.104 register: external_fip2_result - name: Verify floating ip got updated assert: that: - external_fip2_result is changed - external_fip2_result.floating_ip.floating_ip_address == "10.6.6.151" - external_fip2_result.floating_ip.port_id == port4.port.id - external_fip2_result.floating_ip.fixed_ip_address == "10.7.7.104" - name: Update floating ip 2 on external network again openstack.cloud.floating_ip: cloud: "{{ cloud }}" state: present network: ansible_external floating_ip_address: 10.6.6.151 nat_destination: ansible_internal fixed_address: 10.7.7.104 register: external_fip2_result - name: Verify idempotency assert: that: - external_fip2_result is not changed - name: Detatch floating ip 2 on external network from port openstack.cloud.floating_ip: cloud: "{{ cloud }}" state: absent network: ansible_external floating_ip_address: 10.6.6.151 - name: Get floating ip 2 info openstack.cloud.floating_ip_info: cloud: "{{ cloud }}" floating_ip_address: 10.6.6.151 register: external_fip2_result - name: Verify floating ip got detached assert: that: - external_fip2_result.floating_ips.0.floating_ip_address == "10.6.6.151" - external_fip2_result.floating_ips.0.fixed_ip_address == none - external_fip2_result.floating_ips.0.port_id == none - name: Detatch floating ip 2 on external network from port again openstack.cloud.floating_ip: cloud: "{{ cloud }}" state: absent network: ansible_external floating_ip_address: 10.6.6.151 - name: Get floating ip 2 info openstack.cloud.floating_ip_info: cloud: "{{ cloud }}" floating_ip_address: 10.6.6.151 register: external_fip2_result - name: Verify idempotency assert: that: - external_fip2_result is not changed - name: Create server 1 with one nic openstack.cloud.server: cloud: "{{ cloud }}" state: present name: ansible_server1 image: "{{ image_name }}" flavor: m1.tiny nics: # one nic only else simple, first floating ip test does not work - port-name: ansible_internal_port1 auto_ip: false wait: true register: server1 - name: Get server 1 ports openstack.cloud.port_info: cloud: "{{ cloud }}" filters: device_id: "{{ server1.server.id }}" register: server1_ports - name: Assert one fixed ip on server 1 # If this assertion fails because server has an public ipv4 address (public_v4) then make sure # that no floating ip on public network is associated with "10.7.7.100" before running this role assert: that: - server1_ports.ports|length == 1 - server1_ports.ports|sum(attribute='fixed_ips', start=[])|map(attribute='ip_address')|sort|list == ["10.7.7.100"] - name: Create server 2 with two nics openstack.cloud.server: cloud: "{{ cloud }}" state: present name: ansible_server2 image: "{{ image_name }}" flavor: m1.tiny nics: - port-name: ansible_internal_port2 - port-name: ansible_internal_port3 auto_ip: false wait: true register: server2 - name: Get server 2 ports openstack.cloud.port_info: cloud: "{{ cloud }}" filters: device_id: "{{ server2.server.id }}" register: server2_ports - name: Assert two fixed ips on server 2 assert: that: - server2_ports.ports|length == 2 - server2_ports.ports|sum(attribute='fixed_ips', start=[])|map(attribute='ip_address')|sort|list == ["10.7.7.101", "10.7.7.102"] - name: Assign new floating IP to server from first available external network or nova pool openstack.cloud.floating_ip: cloud: "{{ cloud }}" state: present server: ansible_server1 wait: true - name: Get floating ip attached to server 1 openstack.cloud.floating_ip_info: cloud: "{{ cloud }}" port: "{{ port1.port.id }}" register: server1_fips # openstacksdk has issues with waiting hence we simply retry retries: 10 delay: 3 until: server1_fips.floating_ips|length == 1 - name: Assert fixed ip and floating ip attached to server 1 assert: that: - server1_ports.ports|length == 1 - server1_ports.ports|sum(attribute='fixed_ips', start=[])|map(attribute='ip_address')|sort|list == ["10.7.7.100"] - server1_fips.floating_ips|length == 1 - server1_fips.floating_ips|map(attribute='fixed_ip_address')|sort|list == ["10.7.7.100"] - name: Assert return values of floating_ip_info module assert: that: - server1_fips is success - server1_fips is not changed - server1_fips.floating_ips|length > 0 # allow new fields to be introduced but prevent fields from being removed - expected_fields|difference(server1_fips.floating_ips[0].keys())|length == 0 - name: Assign floating ip to server 1 again openstack.cloud.floating_ip: cloud: "{{ cloud }}" state: present server: ansible_server1 wait: true register: floating_ip - name: Assert floating ip on server 1 has not changed assert: that: floating_ip is not changed - name: Assert return values of floating_ip module assert: that: - floating_ip.floating_ip|length > 0 # allow new fields to be introduced but prevent fields from being removed - expected_fields|difference(floating_ip.floating_ip.keys())|length == 0 - name: Detach floating ip from server 1 openstack.cloud.floating_ip: cloud: "{{ cloud }}" state: absent server: ansible_server1 network: public floating_ip_address: "{{ server1_fips.floating_ips.0.floating_ip_address }}" - name: Wait until floating ip is detached from server 1 openstack.cloud.floating_ip_info: cloud: "{{ cloud }}" port: "{{ port1.port.id }}" register: server1_fips # When detaching a floating ip from an instance there might be a delay until it is not listed anymore retries: 10 delay: 3 until: server1_fips.floating_ips|length == 0 - name: Find all floating ips for debugging openstack.cloud.floating_ip_info: cloud: "{{ cloud }}" register: fips - name: Print all floating ips for debugging debug: var=fips - name: Find all servers for debugging openstack.cloud.server_info: cloud: "{{ cloud }}" register: servers - name: Print all servers for debugging debug: var=servers - name: Assign floating ip to server 2 openstack.cloud.floating_ip: cloud: "{{ cloud }}" state: present reuse: false # else fixed_address will be ignored server: ansible_server2 network: public fixed_address: "{{ port2.port.fixed_ips[0].ip_address }}" wait: true register: server2_fip - name: Assert floating ip attached to server 2 assert: that: - server2_fip.floating_ip|length > 0 - name: Find all floating ips for debugging openstack.cloud.floating_ip_info: cloud: "{{ cloud }}" register: fips - name: Print all floating ips for debugging debug: var=fips - name: Find all servers for debugging openstack.cloud.server_info: cloud: "{{ cloud }}" register: servers - name: Print all servers for debugging debug: var=servers - name: Get floating ip attached to server 2 openstack.cloud.floating_ip_info: cloud: "{{ cloud }}" port: "{{ port2.port.id }}" register: server2_fips - name: Assert floating ip attached to server 2 assert: that: - server2_fips.floating_ips|length == 1 - server2_fips.floating_ips|map(attribute='fixed_ip_address')|sort|list == ["10.7.7.101"] - name: Assign a second, specific floating ip to server 2 openstack.cloud.floating_ip: cloud: "{{ cloud }}" state: present reuse: false # else fixed_address will be ignored server: ansible_server2 network: ansible_external fixed_address: "{{ port3.port.fixed_ips[0].ip_address }}" floating_ip_address: "10.6.6.150" wait: false # does not work anyway and causes issues in local testing - name: Get floating ip attached to server 2 openstack.cloud.floating_ip_info: cloud: "{{ cloud }}" port: "{{ port3.port.id }}" register: server2_fips # We cannot wait for second floating ip to be attached because OpenStackSDK checks only for first floating ip # Ref.: https://github.com/openstack/openstacksdk/blob/e0372b72af8c5f471fc17e53434d7a814ca958bd/openstack/cloud/_floating_ip.py#L733 retries: 10 delay: 3 until: server2_fips.floating_ips|length == 1 - name: Assert second floating ip attached to server 2 assert: that: - server2_fips.floating_ips|length == 1 - server2_fips.floating_ips|map(attribute='fixed_ip_address')|sort|list == ["10.7.7.102"] - name: Detach second floating ip from server 2 openstack.cloud.floating_ip: cloud: "{{ cloud }}" state: absent server: ansible_server2 network: ansible_external floating_ip_address: "10.6.6.150" - name: Wait until second floating ip is detached from server 2 openstack.cloud.floating_ip_info: cloud: "{{ cloud }}" port: "{{ port3.port.id }}" register: server2_fips # When detaching a floating ip from an instance there might be a delay until it is not listed anymore retries: 10 delay: 3 until: server2_fips.floating_ips|length == 0 - name: Get first floating ip attached to server 2 openstack.cloud.floating_ip_info: cloud: "{{ cloud }}" port: "{{ port2.port.id }}" register: server2_fips - name: Detach remaining floating ip from server 2 openstack.cloud.floating_ip: cloud: "{{ cloud }}" state: absent server: ansible_server2 network: public floating_ip_address: "{{ server2_fips.floating_ips.0.floating_ip_address }}" - name: Wait until first floating ip is detached from server 2 openstack.cloud.floating_ip_info: cloud: "{{ cloud }}" port: "{{ port2.port.id }}" register: server2_fips # When detaching a floating ip from an instance there might be a delay until it is not listed anymore retries: 10 delay: 3 until: server2_fips.floating_ips|length == 0 - name: Delete server with two nics openstack.cloud.server: cloud: "{{ cloud }}" state: absent name: ansible_server2 wait: true - name: Delete server with one nic openstack.cloud.server: cloud: "{{ cloud }}" state: absent name: ansible_server1 wait: true - name: Get all floating ips openstack.cloud.floating_ip_info: cloud: "{{ cloud }}" register: fips - name: Delete floating ip on public network if we created it when: not public_network_had_fips openstack.cloud.floating_ip: cloud: "{{ cloud }}" state: absent purge: true floating_ip_address: "{{ public_fip }}" network: public loop: >- {{ fips.floating_ips | selectattr('floating_network_id', '==', public_network.networks.0.id) | map(attribute="floating_ip_address") | list }} loop_control: loop_var: public_fip - name: Delete floating ip 1 when: fips.floating_ips|length > 0 and "10.6.6.150" in fips.floating_ips|map(attribute="floating_ip_address")|list openstack.cloud.floating_ip: cloud: "{{ cloud }}" state: absent purge: true floating_ip_address: 10.6.6.150 network: ansible_external - name: Delete floating ip 2 when: fips.floating_ips|length > 0 and "10.6.6.151" in fips.floating_ips|map(attribute="floating_ip_address")|list openstack.cloud.floating_ip: cloud: "{{ cloud }}" state: absent purge: true floating_ip_address: 10.6.6.151 network: ansible_external - name: Get remaining floating ips on external network openstack.cloud.floating_ip_info: cloud: "{{ cloud }}" floating_network: ansible_external register: fips # The first, simple floating ip test might have allocated a floating ip on the external network. # This floating ip must be removed before external network can be deleted. - name: Delete remaining floating ips on external network when: fips.floating_ips|length > 0 openstack.cloud.floating_ip: cloud: "{{ cloud }}" state: absent purge: true floating_ip_address: "{{ external_fip }}" network: ansible_external loop: >- {{ fips.floating_ips | map(attribute="floating_ip_address") | list }} loop_control: loop_var: external_fip # Remove routers after floating ips have been detached and disassociated else removal fails with # Error detaching interface from router ***: Client Error for url: ***, # Router interface for subnet *** on router *** cannot be deleted, # as it is required by one or more floating IPs. - name: Delete router 2 openstack.cloud.router: cloud: "{{ cloud }}" state: absent name: ansible_router2 - name: Delete router 1 openstack.cloud.router: cloud: "{{ cloud }}" state: absent name: ansible_router1 - name: Delete internal port 4 openstack.cloud.port: cloud: "{{ cloud }}" state: absent name: ansible_internal_port4 - name: Delete internal port 3 openstack.cloud.port: cloud: "{{ cloud }}" state: absent name: ansible_internal_port3 - name: Delete internal port 2 openstack.cloud.port: cloud: "{{ cloud }}" state: absent name: ansible_internal_port2 - name: Delete internal port 1 openstack.cloud.port: cloud: "{{ cloud }}" state: absent name: ansible_internal_port1 - name: Delete internal subnet openstack.cloud.subnet: cloud: "{{ cloud }}" state: absent name: ansible_internal_subnet - name: Delete internal network openstack.cloud.network: cloud: "{{ cloud }}" state: absent name: ansible_internal - name: Delete external port 2 openstack.cloud.port: cloud: "{{ cloud }}" state: absent name: ansible_external_port2 - name: Delete external port 1 openstack.cloud.port: cloud: "{{ cloud }}" state: absent name: ansible_external_port1 - name: Delete external subnet openstack.cloud.subnet: cloud: "{{ cloud }}" state: absent name: ansible_external_subnet - name: Delete external network openstack.cloud.network: cloud: "{{ cloud }}" state: absent name: ansible_external