Files
ansible-collections-openstack/ci/roles/router/tasks/main.yml
Andrew Bonney 98bb212ae4 Fix router module external IPs when only subnet specified
The router module used to support adding external fixed IPs
by specifying the subnet only, and the documentation still
allows for this. Unfortunately the logic currently results
in an error message stating that the ip_address cannot be
null.

This patch reinstates this functionality and adds tests to
avoid a future regression.

Change-Id: Ie29c7b2b763a58ea107cc50507e99f650ee9e53f
2025-04-22 16:28:02 +00:00

765 lines
20 KiB
YAML

---
# Ensure clean environment
- name: Ensure router doesn't exist before tests
openstack.cloud.router:
cloud: "{{ cloud }}"
state: absent
name: "{{ router_name }}"
- name: Find network
openstack.cloud.networks_info:
cloud: "{{ cloud }}"
name: "{{ network_name }}"
register: networks
- name: Get ports in internal network
when: networks.networks|length > 0
openstack.cloud.port_info:
cloud: "{{ cloud }}"
filters:
network_id: "{{ networks.networks.0.id }}"
register: existing_ports
- name: Ensure ports don't exist before tests
when: networks.networks|length > 0
openstack.cloud.port:
cloud: "{{ cloud }}"
name: "{{ item.id }}"
state: absent
loop: "{{ existing_ports.ports }}"
- name: Delete subnets before tests
openstack.cloud.subnet:
cloud: "{{ cloud }}"
state: absent
name: "{{ item.name }}"
loop: "{{ test_subnets }}"
# Regular user operation
- name: Create internal network
openstack.cloud.network:
cloud: "{{ cloud }}"
state: present
name: "{{ network_name }}"
external: false
- name: Create subnets 1-4
openstack.cloud.subnet: "{{ item }}"
loop: "{{ test_subnets }}"
- name: Ensure router doesn't exist before tests
openstack.cloud.router:
cloud: "{{ cloud }}"
state: absent
name: "{{ router_name }}"
- name: Create router
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
register: router
- name: Verify returned values
assert:
that: item in router.router
loop: "{{ expected_fields }}"
- name: Gather routers info
openstack.cloud.routers_info:
cloud: "{{ cloud }}"
name: "{{ router_name }}"
register: info
- name: Verify routers info
assert:
that:
- info.routers|length == 1
- info.routers.0.name == router_name
- info.routers.0.is_admin_state_up
- name: List ports of router
openstack.cloud.port_info:
cloud: "{{ cloud }}"
filters:
device_id: "{{ router.router.id }}"
register: ports
- name: Verify router ports
assert:
that: ports.ports|length == 0
- name: Update router (add interface)
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
interfaces:
- shade_subnet1
- name: Update router (add interface) again
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
interfaces:
- shade_subnet1
register: router
- name: Assert idempotent module
assert:
that: router is not changed
- name: Gather routers info
openstack.cloud.routers_info:
cloud: "{{ cloud }}"
name: "{{ router_name }}"
register: info
- name: Verify routers info
assert:
that:
- info.routers.0.name == router_name
- info.routers.0.is_admin_state_up
- name: List ports of router
openstack.cloud.port_info:
cloud: "{{ cloud }}"
filters:
device_id: "{{ router.router.id }}"
register: ports
- name: Verify router ports
assert:
that:
- ports.ports|length == 1
- (ports.ports.0.fixed_ips|map(attribute='ip_address')|sort|list == ['10.7.7.1']) or
ports.ports.0.fixed_ips|length > 0
- name: Verify existence of return values
assert:
that: item in info.routers[0]
loop: "{{ expected_fields }}"
- name: Gather routers info with filters
openstack.cloud.routers_info:
cloud: "{{ cloud }}"
filters:
is_admin_state_up: true
name: "{{ router_name }}"
register: info
- name: Verify routers info with filters
assert:
that:
- info.routers.0.name == router_name
- info.routers.0.id == router.router.id
- name: Gather routers info with other filters
openstack.cloud.routers_info:
cloud: "{{ cloud }}"
filters:
is_admin_state_up: false
name: "{{ router_name }}"
register: info
- name: Verify routers info with other filters
assert:
that: info.routers == []
- name: Update router (change interfaces)
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
interfaces:
- net: '{{ network_name }}'
subnet: shade_subnet2
portip: 10.8.8.1
- net: '{{ network_name }}'
subnet: shade_subnet3
- shade_subnet4
- name: Update router (change interfaces) again
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
interfaces:
- net: '{{ network_name }}'
subnet: shade_subnet2
portip: 10.8.8.1
- net: '{{ network_name }}'
subnet: shade_subnet3
- shade_subnet4
register: router
- name: Assert idempotent module
assert:
that: router is not changed
- name: Gather routers info
openstack.cloud.routers_info:
cloud: "{{ cloud }}"
name: "{{ router_name }}"
register: info
- name: Verify routers info
assert:
that:
- info.routers|length == 1
- info.routers.0.name == router_name
- info.routers.0.id == router.router.id
- name: List ports of router
openstack.cloud.port_info:
cloud: "{{ cloud }}"
filters:
device_id: "{{ router.router.id }}"
register: ports
- name: Verify router ports
assert:
that:
- ports.ports|rejectattr('device_owner', 'equalto', 'network:router_gateway')|sum(attribute='fixed_ips', start=[])|map(attribute='ip_address')|list|length == 3
- ports.ports|rejectattr('device_owner', 'equalto', 'network:router_gateway')|sum(attribute='fixed_ips', start=[])|map(attribute='ip_address')|sort|list ==
['10.10.10.1', '10.8.8.1', '10.9.9.1']
- name: Update router (remove interfaces)
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
interfaces:
- shade_subnet4
- name: Update router (remove interfaces) again
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
interfaces:
- shade_subnet4
register: router
- name: Assert idempotent module
assert:
that: router is not changed
- name: Gather routers info
openstack.cloud.routers_info:
cloud: "{{ cloud }}"
name: "{{ router_name }}"
register: info
- name: Verify routers info
assert:
that:
- info.routers.0.name == router_name
- info.routers.0.id == router.router.id
- name: List ports of router
openstack.cloud.port_info:
cloud: "{{ cloud }}"
filters:
device_id: "{{ router.router.id }}"
register: ports
- name: Verify router ports
assert:
that:
- ports.ports|rejectattr('device_owner', 'equalto', 'network:router_gateway')|sum(attribute='fixed_ips', start=[])|map(attribute='ip_address')|list|length == 1
- ports.ports|rejectattr('device_owner', 'equalto', 'network:router_gateway')|sum(attribute='fixed_ips', start=[])|map(attribute='ip_address')|sort|list ==
['10.10.10.1']
- name: Update router (replace interfaces)
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
interfaces:
- net: '{{ network_name }}'
subnet: shade_subnet1
portip: 10.7.7.1
- name: List ports of router
openstack.cloud.port_info:
cloud: "{{ cloud }}"
filters:
device_id: "{{ router.router.id }}"
register: ports
- name: Verify router ports
assert:
that:
- ports.ports|rejectattr('device_owner', 'equalto', 'network:router_gateway')|sum(attribute='fixed_ips', start=[])|map(attribute='ip_address')|list|length == 1
- ports.ports|rejectattr('device_owner', 'equalto', 'network:router_gateway')|sum(attribute='fixed_ips', start=[])|map(attribute='ip_address')|sort|list ==
['10.7.7.1']
- name: Update router (replace interfaces) again
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
interfaces:
- net: '{{ network_name }}'
subnet: shade_subnet1
portip: 10.7.7.1
register: router
- name: Assert idempotent module
assert:
that: router is not changed
- name: List ports of router
openstack.cloud.port_info:
cloud: "{{ cloud }}"
filters:
device_id: "{{ router.router.id }}"
register: ports
- name: Verify router ports
assert:
that:
- ports.ports|rejectattr('device_owner', 'equalto', 'network:router_gateway')|sum(attribute='fixed_ips', start=[])|map(attribute='ip_address')|list|length == 1
- ports.ports|rejectattr('device_owner', 'equalto', 'network:router_gateway')|sum(attribute='fixed_ips', start=[])|map(attribute='ip_address')|sort|list ==
['10.7.7.1']
# Admin operation
- name: Create external network
openstack.cloud.network:
cloud: "{{ cloud }}"
state: present
name: "{{ external_network_name }}"
external: true
- name: Create subnet5
openstack.cloud.subnet:
cloud: "{{ cloud }}"
state: present
network_name: "{{ external_network_name }}"
name: shade_subnet5
cidr: 10.6.6.0/24
- name: Update router (add external gateway)
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
network: "{{ external_network_name }}"
interfaces:
- shade_subnet1
- name: Gather routers info
openstack.cloud.routers_info:
cloud: "{{ cloud }}"
name: "{{ router_name }}"
filters:
is_admin_state_up: true
register: info
- name: Verify routers info
assert:
that:
- info.routers.0.name == router_name
- name: List ports of router
openstack.cloud.port_info:
cloud: "{{ cloud }}"
filters:
device_id: "{{ router.router.id }}"
register: ports
- name: Verify router ports
assert:
that:
- ports.ports|rejectattr('device_owner', 'equalto', 'network:router_gateway')|sum(attribute='fixed_ips', start=[])|map(attribute='ip_address')|list|length == 1
- name: Update router (change external fixed ips)
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
interfaces:
- shade_subnet1
network: "{{ external_network_name }}"
external_fixed_ips:
- subnet_id: shade_subnet5
ip: 10.6.6.100
- name: Gather routers info
openstack.cloud.routers_info:
cloud: "{{ cloud }}"
name: "{{ router_name }}"
filters:
is_admin_state_up: true
register: info
- name: Verify routers info
assert:
that:
- info.routers.0.name == router_name
- (info.routers.0.external_gateway_info.external_fixed_ips|length) == 1
- info.routers.0.external_gateway_info.external_fixed_ips.0.ip_address == "10.6.6.100"
- name: Update router (add external fixed ips)
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
interfaces:
- shade_subnet1
external_gateway_info:
network: "{{ external_network_name }}"
external_fixed_ips:
- subnet_id: shade_subnet5
ip: 10.6.6.100
- subnet: shade_subnet5
ip: 10.6.6.101
- name: Update router (add external fixed ips) again
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
interfaces:
- shade_subnet1
network: "{{ external_network_name }}"
external_fixed_ips:
- subnet_id: shade_subnet5
ip: 10.6.6.100
- subnet: shade_subnet5
ip: 10.6.6.101
register: router
- name: Assert idempotent module
assert:
that: router is not changed
- name: Gather routers info
openstack.cloud.routers_info:
cloud: "{{ cloud }}"
name: "{{ router_name }}"
filters:
is_admin_state_up: true
register: info
- name: Verify routers info
assert:
that:
- info.routers.0.name == router_name
- (info.routers.0.external_gateway_info.external_fixed_ips|length) == 2
- info.routers.0.external_gateway_info.external_fixed_ips|map(attribute='ip_address')|sort|list ==
["10.6.6.100", "10.6.6.101"]
- name: Update router (remove external fixed ips)
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
interfaces:
- shade_subnet1
network: "{{ external_network_name }}"
external_fixed_ips:
- subnet_id: shade_subnet5
ip: 10.6.6.101
- name: Update router (remove external fixed ips) again
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
interfaces:
- shade_subnet1
network: "{{ external_network_name }}"
external_fixed_ips:
- subnet_id: shade_subnet5
ip: 10.6.6.101
register: router
- name: Assert idempotent module
assert:
that: router is not changed
- name: Gather routers info
openstack.cloud.routers_info:
cloud: "{{ cloud }}"
name: "{{ router_name }}"
filters:
is_admin_state_up: true
register: info
- name: Verify routers info
assert:
that:
- info.routers.0.name == router_name
- (info.routers.0.external_gateway_info.external_fixed_ips|length) == 1
- info.routers.0.external_gateway_info.external_fixed_ips.0.ip_address == "10.6.6.101"
- name: Update router (disable external snat)
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
enable_snat: false
interfaces:
- shade_subnet1
network: "{{ external_network_name }}"
external_fixed_ips:
- subnet_id: shade_subnet5
ip: 10.6.6.101
- name: Gather routers info
openstack.cloud.routers_info:
cloud: "{{ cloud }}"
name: "{{ router_name }}"
filters:
is_admin_state_up: true
register: info
- name: Verify routers info
assert:
that:
- info.routers.0.name == router_name
- not info.routers.0.external_gateway_info.enable_snat
- name: Update router (disable external snat) again
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
enable_snat: false
interfaces:
- shade_subnet1
network: "{{ external_network_name }}"
external_fixed_ips:
- subnet_id: shade_subnet5
ip: 10.6.6.101
register: router
- name: Assert idempotent module
assert:
that: router is not changed
- name: Delete router
openstack.cloud.router:
cloud: "{{ cloud }}"
state: absent
name: "{{ router_name }}"
- name: Delete router again
openstack.cloud.router:
cloud: "{{ cloud }}"
state: absent
name: "{{ router_name }}"
register: router
- name: Assert idempotent module
assert:
that: router is not changed
- name: Create router without explicit IP address
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
enable_snat: false
interfaces:
- shade_subnet1
network: "{{ external_network_name }}"
external_fixed_ips:
- subnet_id: shade_subnet5
register: router
- name: Assert idempotent module
assert:
that: router is changed
- name: Update router without explicit IP address
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
enable_snat: false
interfaces:
- shade_subnet1
network: "{{ external_network_name }}"
external_fixed_ips:
- subnet_id: shade_subnet5
register: router
- name: Assert idempotent module
assert:
that: router is not changed
- name: Delete router
openstack.cloud.router:
cloud: "{{ cloud }}"
state: absent
name: "{{ router_name }}"
- name: Create router with simple interface
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
interfaces:
- shade_subnet1
register: router
- name: Assert changed
assert:
that: router is changed
- name: Set portip in already assigned subnet
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
interfaces:
- subnet: shade_subnet1
net: "{{ network_name }}"
portip: 10.7.7.42
register: router
- name: Assert changed
assert:
that: router is changed
- name: List ports of router
openstack.cloud.port_info:
cloud: "{{ cloud }}"
filters:
device_id: "{{ router.router.id }}"
register: ports
- name: Verify router ports
assert:
that:
- ports.ports|rejectattr('device_owner', 'equalto', 'network:router_gateway')|sum(attribute='fixed_ips', start=[])|map(attribute='ip_address')|list|length == 1
- ports.ports|rejectattr('device_owner', 'equalto', 'network:router_gateway')|sum(attribute='fixed_ips', start=[])|map(attribute='ip_address')|sort|list ==
['10.7.7.42']
- name: Unset portip in already assigned subnet
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
interfaces:
- subnet: shade_subnet1
net: "{{ network_name }}"
register: router
- name: Assert not changed
assert:
that: router is not changed
- name: List all routers
openstack.cloud.routers_info:
cloud: "{{ cloud }}"
register: routers
- name: List ports of all routers
loop: "{{ routers.routers }}"
openstack.cloud.port_info:
cloud: "{{ cloud }}"
filters:
device_id: "{{ item['id'] }}"
register: ports
- name: Transform ports for interfaces_info entries
loop: "{{ ports.results|map(attribute='ports')|list }}"
set_fact:
interfaces_info: |-
{% for port in item %}
{% if port.device_owner != "network:router_gateway" %}
{% for fixed_ip in port['fixed_ips'] %}
- port_id: {{ port.id }}
ip_address: {{ fixed_ip.ip_address }}
subnet_id: {{ fixed_ip.subnet_id }}
{% endfor %}
{% endif %}
{% endfor %}
register: interfaces
- name: Combine router and interfaces_info entries
loop: "{{
routers.routers|zip(interfaces.results|map(attribute='ansible_facts'))|list
}}"
set_fact:
# underscore prefix to prevent overwriting facts outside of loop
_router: "{{
item.0|combine({'interfaces_info': (item.1.interfaces_info|from_yaml) })
}}"
register: routers
- name: Remove set_fact artifacts from routers
set_fact:
routers: "{{ {
'routers': routers.results|map(attribute='ansible_facts._router')|list
} }}"
- debug: var=routers
- name: Assert our router's interfaces_info
assert:
that:
- routers.routers|selectattr('id', 'equalto', router.router.id)|list|length == 1
# Cleanup environment
- name: Delete router
openstack.cloud.router:
cloud: "{{ cloud }}"
state: absent
name: "{{ router_name }}"
- name: Find network
openstack.cloud.networks_info:
cloud: "{{ cloud }}"
name: "{{ network_name }}"
register: networks
- name: Get ports in internal network
openstack.cloud.port_info:
cloud: "{{ cloud }}"
filters:
network_id: "{{ networks.networks.0.id }}"
register: existing_ports
- name: Clean up ports
openstack.cloud.port:
cloud: "{{ cloud }}"
name: "{{ item.id }}"
state: absent
loop: "{{ existing_ports.ports }}"
- name: Delete subnet5
openstack.cloud.subnet:
cloud: "{{ cloud }}"
state: absent
name: shade_subnet5
- name: Delete subnets 1-4
openstack.cloud.subnet:
cloud: "{{ cloud }}"
state: absent
name: "{{ item.name }}"
loop: "{{ test_subnets }}"
- name: Delete internal network
openstack.cloud.network:
cloud: "{{ cloud }}"
state: absent
name: "{{ network_name }}"
- name: Delete external network
openstack.cloud.network:
cloud: "{{ cloud }}"
state: absent
name: "{{ external_network_name }}"
- include_tasks: shared_network.yml
- include_tasks: shared_ext_network.yml