Refactored floating_ip and floating_ip_info modules
Use service proxies from openstacksdk wherever reasonable in order to reduce calls to OpenStack API. Renamed floating_ip_info's attribute 'project_id' to 'project' to be consistent with other attributes and added the former as an alias to keep backward compatibility. The latter can now also be used to search for floating ips by project names, not only project ids. Sorted argument specs and documentation of both modules. Reworked integration tests, e.g. replaced references to server's 'addresses' attribute with calls to our port_info and floating_ip_info modules. Also reformatted tests and added assertion on return values. Merged integration tests of floating_ip_info module into floating_ip module, because the former does not create any floating ips and assumes that they have been created earlier. For Zuul CI job ansible-collections-openstack-functional-devstack-\ releases to pass, the minimum required openstacksdk release must be 0.102.0 because [1],[2],[3],[4] are available since that release only. [1] https://review.opendev.org/c/openstack/openstacksdk/+/851976 [2]0ded7ac398
[3] https://review.opendev.org/c/openstack/openstacksdk/+/859672 [4]2535ba7a28
Change-Id: I129f866e7ed8d5c0499c93e78ebbe2c424e09423
This commit is contained in:
parent
0ade33eb6f
commit
34b0abb4ca
@ -71,7 +71,7 @@
|
||||
dns
|
||||
dns_zone_info
|
||||
endpoint
|
||||
floating_ip_info
|
||||
floating_ip
|
||||
host_aggregate
|
||||
identity_domain_info
|
||||
identity_group
|
||||
@ -112,7 +112,6 @@
|
||||
user_role
|
||||
volume
|
||||
# failing tags
|
||||
# floating_ip
|
||||
# neutron_rbac
|
||||
|
||||
- job:
|
||||
|
21
ci/roles/floating_ip/defaults/main.yml
Normal file
21
ci/roles/floating_ip/defaults/main.yml
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
expected_fields:
|
||||
- created_at
|
||||
- description
|
||||
- dns_domain
|
||||
- dns_name
|
||||
- fixed_ip_address
|
||||
- floating_ip_address
|
||||
- floating_network_id
|
||||
- id
|
||||
- name
|
||||
- port_details
|
||||
- port_id
|
||||
- project_id
|
||||
- qos_policy_id
|
||||
- revision_number
|
||||
- router_id
|
||||
- status
|
||||
- subnet_id
|
||||
- tags
|
||||
- updated_at
|
@ -1,5 +1,4 @@
|
||||
---
|
||||
# Prepare environment
|
||||
- name: Gather information about public network
|
||||
openstack.cloud.networks_info:
|
||||
cloud: "{{ cloud }}"
|
||||
@ -12,109 +11,112 @@
|
||||
|
||||
- name: Create external network
|
||||
openstack.cloud.network:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_external
|
||||
external: true
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_external
|
||||
external: true
|
||||
|
||||
- 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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_internal_port3
|
||||
network: ansible_internal
|
||||
fixed_ips:
|
||||
- ip_address: 10.7.7.102
|
||||
register: port3
|
||||
|
||||
- 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
|
||||
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
|
||||
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 }}"
|
||||
cloud: "{{ cloud }}"
|
||||
register: fips
|
||||
|
||||
- name: Check if public network has any floating ips
|
||||
@ -138,232 +140,286 @@
|
||||
when: fips.floating_ips|length == 0 or
|
||||
"10.6.6.150" not in fips.floating_ips|map(attribute="floating_ip_address")|list
|
||||
|
||||
- name: Create server with one nic
|
||||
- name: Create server 1 with one nic
|
||||
openstack.cloud.server:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_server1
|
||||
image: "{{ image }}"
|
||||
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
|
||||
|
||||
- name: Get info about server
|
||||
openstack.cloud.server_info:
|
||||
cloud: "{{ cloud }}"
|
||||
server: ansible_server1
|
||||
register: info
|
||||
state: present
|
||||
name: ansible_server1
|
||||
image: "{{ image }}"
|
||||
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: Assert one internal port and no floating ips on server 1
|
||||
- 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:
|
||||
- info.servers|length == 1
|
||||
- info.servers.0.public_v4|length == 0
|
||||
- info.servers.0.public_v6|length == 0
|
||||
- info.servers.0.addresses.ansible_internal|length == 1
|
||||
- info.servers.0.addresses.ansible_internal|map(attribute="addr")|sort|list == ["10.7.7.100"]
|
||||
- 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 with two nics
|
||||
- name: Create server 2 with two nics
|
||||
openstack.cloud.server:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_server2
|
||||
image: "{{ image }}"
|
||||
flavor: m1.tiny
|
||||
nics:
|
||||
- port-name: ansible_internal_port2
|
||||
- port-name: ansible_internal_port3
|
||||
auto_ip: false
|
||||
wait: true
|
||||
|
||||
- name: Get info about server
|
||||
openstack.cloud.server_info:
|
||||
cloud: "{{ cloud }}"
|
||||
server: ansible_server2
|
||||
register: info
|
||||
state: present
|
||||
name: ansible_server2
|
||||
image: "{{ image }}"
|
||||
flavor: m1.tiny
|
||||
nics:
|
||||
- port-name: ansible_internal_port2
|
||||
- port-name: ansible_internal_port3
|
||||
auto_ip: false
|
||||
wait: true
|
||||
register: server2
|
||||
|
||||
- name: Assert two internal ports and no floating ips on server 2
|
||||
- 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:
|
||||
- info.servers|length == 1
|
||||
- info.servers.0.public_v4|length == 0
|
||||
- info.servers.0.public_v6|length == 0
|
||||
- info.servers.0.addresses.ansible_internal|length == 2
|
||||
- info.servers.0.addresses.ansible_internal|map(attribute="addr")|sort|list ==
|
||||
- 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"]
|
||||
|
||||
# Tests
|
||||
- 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 info about server
|
||||
openstack.cloud.server_info:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
server: ansible_server1
|
||||
register: info
|
||||
wait: yes
|
||||
|
||||
- name: Assert one internal port and one floating ip on server 1
|
||||
- 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:
|
||||
- info.servers.0.addresses.ansible_internal|length == 2
|
||||
- info.servers.0.addresses.ansible_internal|map(attribute="OS-EXT-IPS:type")|sort|list ==
|
||||
["fixed", "floating"]
|
||||
- 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: Detach floating IP from server
|
||||
- name: Assert return values of floating_ip_info module
|
||||
assert:
|
||||
that:
|
||||
- server1_fips is success
|
||||
- server1_fips is not changed
|
||||
- server1_fips.floating_ips
|
||||
# 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: absent
|
||||
server: ansible_server1
|
||||
network: public
|
||||
floating_ip_address: "{{ (info.servers.0.addresses.ansible_internal|
|
||||
selectattr('OS-EXT-IPS:type', '==', 'floating')|map(attribute='addr')|list)[0] }}"
|
||||
|
||||
- name: Get info about server
|
||||
openstack.cloud.server_info:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
server: ansible_server1
|
||||
register: info
|
||||
# When detaching a floating ip from an instance there might be a delay until openstack.cloud.server_info
|
||||
# does not list it any more in info.servers.0.addresses.ansible_internal, so retry if necessary.
|
||||
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
|
||||
# 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: info.servers.0.addresses.ansible_internal|length == 1
|
||||
until: server1_fips.floating_ips|length == 0
|
||||
|
||||
- name: Assert one internal port on server 1
|
||||
assert:
|
||||
that:
|
||||
- info.servers.0.addresses.ansible_internal|length == 1
|
||||
- info.servers.0.addresses.ansible_internal|map(attribute="addr")|list == ["10.7.7.100"]
|
||||
- name: Find all floating ips for debugging
|
||||
openstack.cloud.floating_ip_info:
|
||||
cloud: "{{ cloud }}"
|
||||
register: fips
|
||||
|
||||
- name: Assign floating IP to server
|
||||
openstack.cloud.floating_ip:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
reuse: yes
|
||||
server: ansible_server2
|
||||
network: public
|
||||
fixed_address: 10.7.7.101
|
||||
wait: true
|
||||
- name: Print all floating ips for debugging
|
||||
debug: var=fips
|
||||
|
||||
- name: Get info about server
|
||||
- name: Find all servers for debugging
|
||||
openstack.cloud.server_info:
|
||||
cloud: "{{ cloud }}"
|
||||
server: ansible_server2
|
||||
register: info
|
||||
register: servers
|
||||
|
||||
- name: Assert two internal ports and one floating ip on server 2
|
||||
- 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: no # 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:
|
||||
- info.servers.0.addresses.ansible_internal|length == 3
|
||||
- info.servers.0.addresses.ansible_internal|map(attribute="OS-EXT-IPS:type")|sort|list ==
|
||||
["fixed", "fixed", "floating"]
|
||||
- server2_fip.floating_ip
|
||||
|
||||
- name: Assign a second, specific floating IP to server
|
||||
openstack.cloud.floating_ip:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
reuse: yes
|
||||
server: ansible_server2
|
||||
network: ansible_external
|
||||
fixed_address: 10.7.7.102
|
||||
floating_ip_address: "10.6.6.150"
|
||||
- name: Find all floating ips for debugging
|
||||
openstack.cloud.floating_ip_info:
|
||||
cloud: "{{ cloud }}"
|
||||
register: 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
|
||||
- name: Print all floating ips for debugging
|
||||
debug: var=fips
|
||||
|
||||
- name: Get info about server
|
||||
- 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: no # else fixed_address will be ignored
|
||||
server: ansible_server2
|
||||
register: info
|
||||
# retry because we cannot wait for second floating ip
|
||||
network: ansible_external
|
||||
fixed_address: "{{ port3.port.fixed_ips[0].ip_address }}"
|
||||
floating_ip_address: "10.6.6.150"
|
||||
wait: no # 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: info.servers.0.addresses.ansible_internal|length == 4
|
||||
until: server2_fips.floating_ips|length == 1
|
||||
|
||||
- name: Assert two internal ports and two floating ips on server 2
|
||||
- name: Assert second floating ip attached to server 2
|
||||
assert:
|
||||
that:
|
||||
- info.servers.0.addresses.ansible_internal|length == 4
|
||||
- ("10.6.6.150" in info.servers.0.addresses.ansible_internal|map(attribute="addr")|sort|list)
|
||||
- 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
|
||||
- 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: Get info about server
|
||||
openstack.cloud.server_info:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
server: ansible_server2
|
||||
register: info
|
||||
# When detaching a floating ip from an instance there might be a delay until openstack.cloud.server_info
|
||||
# does not list it any more in info.servers.0.addresses.ansible_internal, so retry if necessary.
|
||||
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: info.servers.0.addresses.ansible_internal|length == 3
|
||||
until: server2_fips.floating_ips|length == 0
|
||||
|
||||
- name: Assert two internal ports and one floating ip on server 2
|
||||
assert:
|
||||
that:
|
||||
- info.servers.0.addresses.ansible_internal|length == 3
|
||||
|
||||
- name: Detach remaining floating IP from server
|
||||
openstack.cloud.floating_ip:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
server: ansible_server2
|
||||
network: public
|
||||
floating_ip_address: "{{ (info.servers.0.addresses.ansible_internal|
|
||||
selectattr('OS-EXT-IPS:type', '==', 'floating')|map(attribute='addr')|list)[0] }}"
|
||||
|
||||
- name: Get info about server
|
||||
openstack.cloud.server_info:
|
||||
- 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
|
||||
register: info
|
||||
# When detaching a floating ip from an instance there might be a delay until openstack.cloud.server_info
|
||||
# does not list it any more in info.servers.0.addresses.ansible_internal, so retry if necessary.
|
||||
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: info.servers.0.addresses.ansible_internal|length == 2
|
||||
until: server2_fips.floating_ips|length == 0
|
||||
|
||||
- name: Assert two internal ports on server 2
|
||||
assert:
|
||||
that:
|
||||
- info.servers.0.addresses.ansible_internal|length == 2
|
||||
- info.servers.0.addresses.ansible_internal|map(attribute="addr")|list == ["10.7.7.101", "10.7.7.102"]
|
||||
|
||||
# Clean environment
|
||||
- name: Delete server with two nics
|
||||
openstack.cloud.server:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_server2
|
||||
wait: true
|
||||
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
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_server1
|
||||
wait: true
|
||||
|
||||
- name: Get all floating ips
|
||||
openstack.cloud.floating_ip_info:
|
||||
cloud: "{{ cloud }}"
|
||||
cloud: "{{ cloud }}"
|
||||
register: fips
|
||||
|
||||
# TODO: Replace with appropriate Ansible module once available
|
||||
@ -381,8 +437,8 @@
|
||||
|
||||
- name: Get remaining floating ips on external network
|
||||
openstack.cloud.floating_ip_info:
|
||||
cloud: "{{ cloud }}"
|
||||
floating_network: ansible_external
|
||||
cloud: "{{ cloud }}"
|
||||
floating_network: ansible_external
|
||||
register: fips
|
||||
|
||||
# TODO: Replace with appropriate Ansible module once available
|
||||
@ -396,71 +452,71 @@
|
||||
|
||||
# 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,
|
||||
# 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
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_router2
|
||||
|
||||
- name: Delete router 1
|
||||
openstack.cloud.router:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_router1
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_router1
|
||||
|
||||
- name: Delete internal port 3
|
||||
openstack.cloud.port:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_internal_port3
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_internal_port3
|
||||
|
||||
- name: Delete internal port 2
|
||||
openstack.cloud.port:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_internal_port2
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_internal_port2
|
||||
|
||||
- name: Delete internal port 1
|
||||
openstack.cloud.port:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_internal_port1
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_internal_port1
|
||||
|
||||
- name: Delete internal subnet
|
||||
openstack.cloud.subnet:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_internal_subnet
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_internal_subnet
|
||||
|
||||
- name: Delete internal network
|
||||
openstack.cloud.network:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_internal
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_internal
|
||||
|
||||
- name: Delete external port 2
|
||||
openstack.cloud.port:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_external_port2
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_external_port2
|
||||
|
||||
- name: Delete external port 1
|
||||
openstack.cloud.port:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_external_port1
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_external_port1
|
||||
|
||||
- name: Delete external subnet
|
||||
openstack.cloud.subnet:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_external_subnet
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_external_subnet
|
||||
|
||||
- name: Delete external network
|
||||
openstack.cloud.network:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_external
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_external
|
||||
|
@ -1,21 +0,0 @@
|
||||
---
|
||||
- name: Getting info about allocated ips
|
||||
openstack.cloud.floating_ip_info:
|
||||
cloud: "{{ cloud }}"
|
||||
register: fips
|
||||
|
||||
- name: assert result
|
||||
assert:
|
||||
that:
|
||||
- fips is success
|
||||
- fips is not changed
|
||||
|
||||
- name: assert fields
|
||||
when: fips.floating_ips|length > 0
|
||||
assert:
|
||||
that:
|
||||
# allow new fields to be introduced but prevent fields from being removed
|
||||
- '["created_at", "description", "dns_domain", "dns_name", "fixed_ip_address", "floating_ip_address",
|
||||
"floating_network_id", "id", "name", "port_details", "port_id", "project_id", "qos_policy_id",
|
||||
"revision_number", "router_id", "status", "subnet_id", "tags", "updated_at"]|
|
||||
difference(fips.floating_ips.0.keys())|length == 0'
|
@ -17,7 +17,7 @@
|
||||
tags: dns
|
||||
when: sdk_version is version(0.28, '>=')
|
||||
- { role: endpoint, tags: endpoint }
|
||||
- { role: floating_ip_info, tags: floating_ip_info }
|
||||
- { role: floating_ip, tags: floating_ip }
|
||||
- { role: host_aggregate, tags: host_aggregate }
|
||||
- { role: identity_domain_info, tags: identity_domain_info }
|
||||
- { role: identity_group, tags: identity_group }
|
||||
@ -67,5 +67,4 @@
|
||||
- { role: volume, tags: volume }
|
||||
- role: loadbalancer
|
||||
tags: loadbalancer
|
||||
- { role: floating_ip, tags: floating_ip }
|
||||
- { role: quota, tags: quota }
|
||||
|
@ -12,66 +12,59 @@ short_description: Add/Remove floating IP from an instance
|
||||
description:
|
||||
- Add or Remove a floating IP to an instance.
|
||||
- Returns the floating IP when attaching only if I(wait=true).
|
||||
- When detaching a floating IP there might be a delay until an instance does not list the floating IP any more.
|
||||
- When detaching a floating IP there might be a delay until an instance
|
||||
does not list the floating IP any more.
|
||||
options:
|
||||
server:
|
||||
description:
|
||||
- The name or ID of the instance to which the IP address
|
||||
should be assigned.
|
||||
required: true
|
||||
type: str
|
||||
network:
|
||||
description:
|
||||
- The name or ID of a neutron external network or a nova pool name.
|
||||
type: str
|
||||
floating_ip_address:
|
||||
description:
|
||||
- A floating IP address to attach or to detach. When I(state) is present
|
||||
can be used to specify a IP address to attach. I(floating_ip_address)
|
||||
requires I(network) to be set.
|
||||
type: str
|
||||
reuse:
|
||||
description:
|
||||
- When I(state) is present, and I(floating_ip_address) is not present,
|
||||
this parameter can be used to specify whether we should try to reuse
|
||||
a floating IP address already allocated to the project.
|
||||
type: bool
|
||||
default: 'no'
|
||||
fixed_address:
|
||||
description:
|
||||
- To which fixed IP of server the floating IP address should be
|
||||
attached to.
|
||||
type: str
|
||||
floating_ip_address:
|
||||
description:
|
||||
- A floating IP address to attach or to detach. When I(state) is
|
||||
present can be used to specify a IP address to attach.
|
||||
I(floating_ip_address) requires I(network) to be set.
|
||||
type: str
|
||||
nat_destination:
|
||||
description:
|
||||
- The name or id of a neutron private network that the fixed IP to
|
||||
attach floating IP is on
|
||||
aliases: ["fixed_network", "internal_network"]
|
||||
type: str
|
||||
wait:
|
||||
network:
|
||||
description:
|
||||
- When attaching a floating IP address, specify whether to wait for it to appear as attached.
|
||||
- Must be set to C(yes) for the module to return the value of the floating IP when attaching.
|
||||
- The name or ID of a neutron external network or a nova pool name.
|
||||
type: str
|
||||
purge:
|
||||
description:
|
||||
- When I(state) is absent, indicates whether or not to delete the
|
||||
floating IP completely, or only detach it from the server.
|
||||
Default is to detach only.
|
||||
type: bool
|
||||
default: 'no'
|
||||
timeout:
|
||||
reuse:
|
||||
description:
|
||||
- Time to wait for an IP address to appear as attached. See wait.
|
||||
required: false
|
||||
default: 60
|
||||
type: int
|
||||
- When I(state) is present, and I(floating_ip_address) is not present,
|
||||
this parameter can be used to specify whether we should try to reuse
|
||||
a floating IP address already allocated to the project.
|
||||
- When I(reuse) is C(true), I(network) is defined and
|
||||
I(floating_ip_address) is undefined, then C(nat_destination) and
|
||||
C(fixed_address) will be ignored.
|
||||
type: bool
|
||||
default: 'no'
|
||||
server:
|
||||
description:
|
||||
- The name or ID of the instance to which the IP address
|
||||
should be assigned.
|
||||
required: true
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Should the resource be present or absent.
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
type: str
|
||||
purge:
|
||||
description:
|
||||
- When I(state) is absent, indicates whether or not to delete the floating
|
||||
IP completely, or only detach it from the server. Default is to detach only.
|
||||
type: bool
|
||||
default: 'no'
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
@ -120,182 +113,384 @@ EXAMPLES = '''
|
||||
server: cattle001
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
floating_ip:
|
||||
description: Dictionary describing the floating ip address.
|
||||
type: dict
|
||||
returned: success
|
||||
contains:
|
||||
created_at:
|
||||
description: Timestamp at which the floating IP was assigned.
|
||||
type: str
|
||||
description:
|
||||
description: The description of a floating IP.
|
||||
type: str
|
||||
dns_domain:
|
||||
description: The DNS domain.
|
||||
type: str
|
||||
dns_name:
|
||||
description: The DNS name.
|
||||
type: str
|
||||
fixed_ip_address:
|
||||
description: The fixed IP address associated with a floating IP address.
|
||||
type: str
|
||||
floating_ip_address:
|
||||
description: The IP address of a floating IP.
|
||||
type: str
|
||||
floating_network_id:
|
||||
description: The id of the network associated with a floating IP.
|
||||
type: str
|
||||
id:
|
||||
description: Id of the floating ip.
|
||||
type: str
|
||||
name:
|
||||
description: Name of the floating ip.
|
||||
type: str
|
||||
port_details:
|
||||
description: |
|
||||
The details of the port that this floating IP associates
|
||||
with. Present if C(fip-port-details) extension is loaded.
|
||||
type: dict
|
||||
port_id:
|
||||
description: The port ID floating ip associated with.
|
||||
type: str
|
||||
project_id:
|
||||
description: The ID of the project this floating IP is associated with.
|
||||
type: str
|
||||
qos_policy_id:
|
||||
description: The ID of the QoS policy attached to the floating IP.
|
||||
type: str
|
||||
revision_number:
|
||||
description: Revision number.
|
||||
type: str
|
||||
router_id:
|
||||
description: The id of the router floating ip associated with.
|
||||
type: str
|
||||
status:
|
||||
description: |
|
||||
The status of a floating IP, which can be 'ACTIVE' or 'DOWN'.
|
||||
type: str
|
||||
subnet_id:
|
||||
description: The id of the subnet the floating ip associated with.
|
||||
type: str
|
||||
tags:
|
||||
description: List of tags.
|
||||
type: list
|
||||
elements: str
|
||||
updated_at:
|
||||
description: Timestamp at which the floating IP was last updated.
|
||||
type: str
|
||||
'''
|
||||
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||
import itertools
|
||||
|
||||
|
||||
class NetworkingFloatingIPModule(OpenStackModule):
|
||||
argument_spec = dict(
|
||||
fixed_address=dict(),
|
||||
floating_ip_address=dict(),
|
||||
nat_destination=dict(aliases=['fixed_network', 'internal_network']),
|
||||
network=dict(),
|
||||
purge=dict(type='bool', default=False),
|
||||
reuse=dict(type='bool', default=False),
|
||||
server=dict(required=True),
|
||||
state=dict(default='present', choices=['absent', 'present']),
|
||||
network=dict(),
|
||||
floating_ip_address=dict(),
|
||||
reuse=dict(type='bool', default=False),
|
||||
fixed_address=dict(),
|
||||
nat_destination=dict(aliases=['fixed_network', 'internal_network']),
|
||||
wait=dict(type='bool', default=False),
|
||||
timeout=dict(type='int', default=60),
|
||||
purge=dict(type='bool', default=False),
|
||||
)
|
||||
|
||||
module_kwargs = dict(
|
||||
required_if=[
|
||||
['state', 'absent', ['floating_ip_address']]
|
||||
],
|
||||
required_by=dict(
|
||||
floating_ip_address=('network',)
|
||||
)
|
||||
required_by={
|
||||
'floating_ip_address': ('network'),
|
||||
}
|
||||
)
|
||||
|
||||
def _get_floating_ip(self, floating_ip_address):
|
||||
f_ips = self.conn.search_floating_ips(
|
||||
filters={'floating_ip_address': floating_ip_address})
|
||||
def run(self):
|
||||
self._init()
|
||||
if self.params['state'] == 'present':
|
||||
self._create_and_attach()
|
||||
|
||||
if not f_ips:
|
||||
return None
|
||||
else: # self.params['state'] == 'absent'
|
||||
self._detach_and_delete()
|
||||
|
||||
return f_ips[0]
|
||||
def _create_and_attach(self):
|
||||
changed = False
|
||||
fixed_address = self.params['fixed_address']
|
||||
floating_ip_address = self.params['floating_ip_address']
|
||||
nat_destination_name_or_id = self.params['nat_destination']
|
||||
network_id = self.network['id'] if self.network else None
|
||||
|
||||
def _list_floating_ips(self, server):
|
||||
return itertools.chain.from_iterable([
|
||||
(addr['addr'] for addr in server.addresses[net] if addr['OS-EXT-IPS:type'] == 'floating')
|
||||
for net in server.addresses
|
||||
])
|
||||
ips = self._find_ips(
|
||||
server=self.server,
|
||||
floating_ip_address=floating_ip_address,
|
||||
network_id=network_id,
|
||||
fixed_address=fixed_address,
|
||||
nat_destination_name_or_id=nat_destination_name_or_id)
|
||||
|
||||
def _match_floating_ip(self, server,
|
||||
floating_ip_address,
|
||||
network_id,
|
||||
fixed_address,
|
||||
nat_destination):
|
||||
# First floating ip satisfies our requirements
|
||||
ip = ips[0] if ips else None
|
||||
|
||||
if floating_ip_address:
|
||||
return self._get_floating_ip(floating_ip_address)
|
||||
elif not fixed_address and nat_destination:
|
||||
nat_destination_name = self.conn.get_network(nat_destination)['name']
|
||||
return next(
|
||||
(self._get_floating_ip(addr['addr'])
|
||||
for addr in server.addresses.get(nat_destination_name, [])
|
||||
if addr['OS-EXT-IPS:type'] == 'floating'),
|
||||
None)
|
||||
else:
|
||||
# not floating_ip_address and (fixed_address or not nat_destination)
|
||||
# A specific floating ip address has been requested
|
||||
|
||||
# get any of the floating ips that matches fixed_address and/or network
|
||||
f_ip_addrs = self._list_floating_ips(server)
|
||||
f_ips = [f_ip for f_ip in self.conn.list_floating_ips() if f_ip['floating_ip_address'] in f_ip_addrs]
|
||||
return next(
|
||||
(f_ip for f_ip in f_ips
|
||||
if ((fixed_address and f_ip.fixed_ip_address == fixed_address) or not fixed_address)
|
||||
and ((network_id and f_ip.network == network_id) or not network_id)),
|
||||
None)
|
||||
if not ip:
|
||||
# If a specific floating ip address has been requested
|
||||
# and it does not exist yet then create it
|
||||
|
||||
def run(self):
|
||||
server_name_or_id = self.params['server']
|
||||
state = self.params['state']
|
||||
network = self.params['network']
|
||||
floating_ip_address = self.params['floating_ip_address']
|
||||
reuse = self.params['reuse']
|
||||
fixed_address = self.params['fixed_address']
|
||||
nat_destination = self.params['nat_destination']
|
||||
wait = self.params['wait']
|
||||
timeout = self.params['timeout']
|
||||
purge = self.params['purge']
|
||||
|
||||
server = self.conn.get_server(server_name_or_id)
|
||||
if not server:
|
||||
self.fail_json(
|
||||
msg="server {0} not found".format(server_name_or_id))
|
||||
|
||||
# Extract floating ips from server
|
||||
f_ip_addrs = self._list_floating_ips(server)
|
||||
|
||||
# Get details about requested floating ip
|
||||
f_ip = self._get_floating_ip(floating_ip_address) if floating_ip_address else None
|
||||
|
||||
if network:
|
||||
network_id = self.conn.get_network(name_or_id=network)["id"]
|
||||
else:
|
||||
network_id = None
|
||||
|
||||
if state == 'present':
|
||||
if floating_ip_address and f_ip and floating_ip_address in f_ip_addrs:
|
||||
# Floating ip address has been assigned to server
|
||||
self.exit_json(changed=False, floating_ip=f_ip)
|
||||
|
||||
if f_ip and f_ip['attached'] and floating_ip_address not in f_ip_addrs:
|
||||
# Requested floating ip has been attached to different server
|
||||
self.fail_json(msg="floating-ip {floating_ip_address} already has been attached to different server"
|
||||
.format(floating_ip_address=floating_ip_address))
|
||||
|
||||
if not floating_ip_address:
|
||||
# No specific floating ip requested, i.e. if any floating ip is already assigned to server,
|
||||
# check that it matches requirements.
|
||||
|
||||
if not fixed_address and nat_destination:
|
||||
# Check if we have any floating ip on the given nat_destination network
|
||||
nat_destination_name = self.conn.get_network(nat_destination)['name']
|
||||
for addr in server.addresses.get(nat_destination_name, []):
|
||||
if addr['OS-EXT-IPS:type'] == 'floating':
|
||||
# A floating ip address has been assigned to the requested nat_destination
|
||||
f_ip = self._get_floating_ip(addr['addr'])
|
||||
self.exit_json(changed=False, floating_ip=f_ip)
|
||||
# else fixed_address or not nat_destination, hence an
|
||||
# analysis of all floating ips of server is required
|
||||
f_ips = [f_ip for f_ip in self.conn.list_floating_ips() if f_ip['floating_ip_address'] in f_ip_addrs]
|
||||
for f_ip in f_ips:
|
||||
if network_id and f_ip.network != network_id:
|
||||
# requested network does not match network of floating ip
|
||||
continue
|
||||
|
||||
if not fixed_address and not nat_destination:
|
||||
# any floating ip will fullfil these requirements
|
||||
self.exit_json(changed=False, floating_ip=f_ip)
|
||||
|
||||
if fixed_address and f_ip.fixed_ip_address == fixed_address:
|
||||
# a floating ip address has been assigned that points to the requested fixed_address
|
||||
self.exit_json(changed=False, floating_ip=f_ip)
|
||||
|
||||
if floating_ip_address and not f_ip:
|
||||
# openstacksdk's create_ip requires floating_ip_address and floating_network_id to be set
|
||||
self.conn.network.create_ip(floating_ip_address=floating_ip_address, floating_network_id=network_id)
|
||||
# Else floating ip either does not exist or has not been attached yet
|
||||
|
||||
# Both floating_ip_address and network are mutually exclusive in add_ips_to_server, i.e.
|
||||
# add_ips_to_server will ignore floating_ip_address if network is set
|
||||
# Ref.: https://github.com/openstack/openstacksdk/blob/a6b0ece2821ea79330c4067100295f6bdcbe456e/openstack/cloud/_floating_ip.py#L987
|
||||
server = self.conn.add_ips_to_server(
|
||||
server=server,
|
||||
ips=floating_ip_address,
|
||||
ip_pool=network if not floating_ip_address else None,
|
||||
reuse=reuse,
|
||||
fixed_address=fixed_address,
|
||||
wait=wait,
|
||||
timeout=timeout, nat_destination=nat_destination)
|
||||
|
||||
# Update the floating ip status
|
||||
f_ip = self._match_floating_ip(server, floating_ip_address, network_id, fixed_address, nat_destination)
|
||||
self.exit_json(changed=True, floating_ip=f_ip)
|
||||
|
||||
elif state == 'absent':
|
||||
f_ip = self._match_floating_ip(server, floating_ip_address, network_id, fixed_address, nat_destination)
|
||||
if not f_ip:
|
||||
# Nothing to detach
|
||||
self.exit_json(changed=False)
|
||||
changed = False
|
||||
|
||||
if f_ip["fixed_ip_address"]:
|
||||
self.conn.detach_ip_from_server(server_id=server['id'], floating_ip_id=f_ip['id'])
|
||||
# OpenStackSDK sets {"port_id": None} to detach a floating ip from an instance,
|
||||
# but there might be a delay until a server does not list it in addresses any more.
|
||||
|
||||
# Update the floating IP status
|
||||
f_ip = self.conn.get_floating_ip(id=f_ip['id'])
|
||||
# openstacksdk's create_ip requires floating_ip_address
|
||||
# and floating_network_id to be set
|
||||
self.conn.network.create_ip(
|
||||
floating_ip_address=floating_ip_address,
|
||||
floating_network_id=network_id)
|
||||
changed = True
|
||||
|
||||
if purge:
|
||||
self.conn.delete_floating_ip(f_ip['id'])
|
||||
self.exit_json(changed=True)
|
||||
self.exit_json(changed=changed, floating_ip=f_ip)
|
||||
else: # ip
|
||||
# Requested floating ip address exists already
|
||||
|
||||
if ip.port_details and (ip.port_details.status == 'ACTIVE') \
|
||||
and (floating_ip_address not in self._filter_ips(
|
||||
self.server)):
|
||||
# Floating ip address exists and has been attached
|
||||
# but to a different server
|
||||
|
||||
# Requested ip has been attached to different server
|
||||
self.fail_json(
|
||||
msg="Floating ip {0} has been attached to different "
|
||||
"server".format(floating_ip_address))
|
||||
|
||||
if not ip \
|
||||
or floating_ip_address not in self._filter_ips(self.server):
|
||||
# Requested floating ip address does not exist or has not been
|
||||
# assigned to server
|
||||
|
||||
self.conn.add_ip_list(
|
||||
server=self.server,
|
||||
ips=[floating_ip_address],
|
||||
wait=self.params['wait'],
|
||||
timeout=self.params['timeout'],
|
||||
fixed_address=fixed_address)
|
||||
changed = True
|
||||
else:
|
||||
# Requested floating ip address has been assigned to server
|
||||
pass
|
||||
|
||||
elif not ips: # and not floating_ip_address
|
||||
# No specific floating ip has been requested and none of the
|
||||
# floating ips which have been assigned to the server matches
|
||||
# requirements
|
||||
|
||||
# add_ips_to_server() will handle several scenarios:
|
||||
#
|
||||
# If a specific floating ip address has been requested then it
|
||||
# will be attached to the server. The floating ip address has
|
||||
# either been created in previous steps or it already existed.
|
||||
# Ref.: https://github.com/openstack/openstacksdk/blob/
|
||||
# 9d3ee1d32149ba2a8bb3dc894295e180746cdddc/openstack/cloud
|
||||
# /_floating_ip.py#L985
|
||||
#
|
||||
# If no specific floating ip address has been requested, reuse
|
||||
# is allowed and a network has been given (with ip_pool) from
|
||||
# which floating ip addresses will be drawn, then any existing
|
||||
# floating ip address from ip_pool=network which is not
|
||||
# attached to any other server will be attached to the server.
|
||||
# If no such floating ip address exists or if reuse is not
|
||||
# allowed, then a new floating ip address will be created
|
||||
# within ip_pool=network and attached to the server.
|
||||
# Ref.: https://github.com/openstack/openstacksdk/blob/
|
||||
# 9d3ee1d32149ba2a8bb3dc894295e180746cdddc/openstack/cloud/
|
||||
# _floating_ip.py#L981
|
||||
#
|
||||
# If no specific floating ip address has been requested and no
|
||||
# network has been given (with ip_pool) from which floating ip
|
||||
# addresses will be taken, then a floating ip address might be
|
||||
# added to the server, refer to _needs_floating_ip() for
|
||||
# details.
|
||||
# Ref.:
|
||||
# * https://github.com/openstack/openstacksdk/blob/
|
||||
# 9d3ee1d32149ba2a8bb3dc894295e180746cdddc/openstack/cloud/\
|
||||
# _floating_ip.py#L989
|
||||
# * https://github.com/openstack/openstacksdk/blob/
|
||||
# 9d3ee1d32149ba2a8bb3dc894295e180746cdddc/openstack/cloud/
|
||||
# _floating_ip.py#L995
|
||||
#
|
||||
# Both floating_ip_address and network are mutually exclusive
|
||||
# in add_ips_to_server(), i.e.add_ips_to_server will ignore
|
||||
# floating_ip_address if network is not None. To prefer
|
||||
# attaching a specific floating ip address over assigning any
|
||||
# fip, ip_pool is only defined if floating_ip_address is None.
|
||||
# Ref.: https://github.com/openstack/openstacksdk/blob/
|
||||
# a6b0ece2821ea79330c4067100295f6bdcbe456e/openstack/cloud/
|
||||
# _floating_ip.py#L987
|
||||
self.conn.add_ips_to_server(
|
||||
server=self.server,
|
||||
ip_pool=network_id,
|
||||
ips=None, # No specific floating ip requested
|
||||
reuse=self.params['reuse'],
|
||||
fixed_address=fixed_address,
|
||||
wait=self.params['wait'],
|
||||
timeout=self.params['timeout'],
|
||||
nat_destination=nat_destination_name_or_id)
|
||||
changed = True
|
||||
else:
|
||||
# Found one or more floating ips which satisfy requirements
|
||||
pass
|
||||
|
||||
if changed:
|
||||
# update server details such as addresses
|
||||
self.server = self.conn.compute.get_server(self.server)
|
||||
|
||||
# Update the floating ip resource
|
||||
ips = self._find_ips(
|
||||
self.server, floating_ip_address, network_id,
|
||||
fixed_address, nat_destination_name_or_id)
|
||||
|
||||
# ips can be empty, e.g. when server has no private ipv4
|
||||
# address to which a floating ip address can be attached
|
||||
|
||||
self.exit_json(
|
||||
changed=changed,
|
||||
floating_ip=ips[0].to_dict(computed=False) if ips else None)
|
||||
|
||||
def _detach_and_delete(self):
|
||||
ips = self._find_ips(
|
||||
server=self.server,
|
||||
floating_ip_address=self.params['floating_ip_address'],
|
||||
network_id=self.network['id'] if self.network else None,
|
||||
fixed_address=self.params['fixed_address'],
|
||||
nat_destination_name_or_id=self.params['nat_destination'])
|
||||
|
||||
if not ips:
|
||||
# Nothing to detach
|
||||
self.exit_json(changed=False)
|
||||
|
||||
changed = False
|
||||
for ip in ips:
|
||||
if ip['fixed_ip_address']:
|
||||
# Silently ignore that ip might not be attached to server
|
||||
self.conn.compute.remove_floating_ip_from_server(
|
||||
self.server, ip['floating_ip_address'])
|
||||
|
||||
# OpenStackSDK sets {"port_id": None} to detach a floating
|
||||
# ip from an instance, but there might be a delay until a
|
||||
# server does not list it in addresses any more.
|
||||
changed = True
|
||||
|
||||
if self.params['purge']:
|
||||
self.conn.network.delete_ip(ip['id'])
|
||||
changed = True
|
||||
|
||||
self.exit_json(changed=changed)
|
||||
|
||||
def _filter_ips(self, server):
|
||||
# Extract floating ips from server
|
||||
|
||||
def _flatten(lists):
|
||||
return [item for sublist in lists for item in sublist]
|
||||
|
||||
if server['addresses'] is None:
|
||||
# fetch server with details
|
||||
server = self.conn.compute.get_server(server)
|
||||
|
||||
if not server['addresses']:
|
||||
return []
|
||||
|
||||
# Returns a list not an iterator here because
|
||||
# it is iterated several times below
|
||||
return [address['addr']
|
||||
for address in _flatten(server['addresses'].values())
|
||||
if address['OS-EXT-IPS:type'] == 'floating']
|
||||
|
||||
def _find_ips(self,
|
||||
server,
|
||||
floating_ip_address,
|
||||
network_id,
|
||||
fixed_address,
|
||||
nat_destination_name_or_id):
|
||||
# Check which floating ips matches our requirements.
|
||||
# They might or might not be attached to our server.
|
||||
if floating_ip_address:
|
||||
# A specific floating ip address has been requested
|
||||
ip = self.conn.network.find_ip(floating_ip_address)
|
||||
return [ip] if ip else []
|
||||
elif (not fixed_address and nat_destination_name_or_id):
|
||||
# No specific floating ip and no specific fixed ip have been
|
||||
# requested but a private network (nat_destination) has been
|
||||
# given where the floating ip should be attached to.
|
||||
return self._find_ips_by_nat_destination(
|
||||
server, nat_destination_name_or_id)
|
||||
else:
|
||||
# not floating_ip_address
|
||||
# and (fixed_address or not nat_destination_name_or_id)
|
||||
|
||||
# An analysis of all floating ips of server is required
|
||||
return self._find_ips_by_network_id_and_fixed_address(
|
||||
server, fixed_address, network_id)
|
||||
|
||||
def _find_ips_by_nat_destination(self,
|
||||
server,
|
||||
nat_destination_name_or_id):
|
||||
|
||||
if not server['addresses']:
|
||||
return None
|
||||
|
||||
# Check if we have any floating ip on
|
||||
# the given nat_destination network
|
||||
nat_destination = self.conn.network.find_network(
|
||||
nat_destination_name_or_id, ignore_missing=False)
|
||||
|
||||
fips_with_nat_destination = [
|
||||
addr for addr
|
||||
in server['addresses'].get(nat_destination['name'], [])
|
||||
if addr['OS-EXT-IPS:type'] == 'floating']
|
||||
|
||||
if not fips_with_nat_destination:
|
||||
return None
|
||||
|
||||
# One or more floating ip addresses have been assigned
|
||||
# to the requested nat_destination; return the first.
|
||||
return [self.conn.network.find_ip(fip['addr'], ignore_missing=False)
|
||||
for fip in fips_with_nat_destination]
|
||||
|
||||
def _find_ips_by_network_id_and_fixed_address(self,
|
||||
server,
|
||||
fixed_address=None,
|
||||
network_id=None):
|
||||
# Get any of the floating ips that matches fixed_address and/or network
|
||||
ips = [ip for ip in self.conn.network.ips()
|
||||
if ip['floating_ip_address'] in self._filter_ips(server)]
|
||||
|
||||
matching_ips = []
|
||||
for ip in ips:
|
||||
if network_id and ip['floating_network_id'] != network_id:
|
||||
# Requested network does not
|
||||
# match network of floating ip
|
||||
continue
|
||||
|
||||
if not fixed_address: # and not nat_destination_name_or_id
|
||||
# Any floating ip will fullfil these requirements
|
||||
matching_ips.append(ip)
|
||||
|
||||
if (fixed_address and ip['fixed_ip_address'] == fixed_address):
|
||||
# A floating ip address has been assigned that
|
||||
# points to the requested fixed_address
|
||||
matching_ips.append(ip)
|
||||
|
||||
return matching_ips
|
||||
|
||||
def _init(self):
|
||||
server_name_or_id = self.params['server']
|
||||
server = self.conn.compute.find_server(server_name_or_id,
|
||||
ignore_missing=False)
|
||||
# fetch server details such as addresses
|
||||
self.server = self.conn.compute.get_server(server)
|
||||
|
||||
network_name_or_id = self.params['network']
|
||||
if network_name_or_id:
|
||||
self.network = self.conn.network.find_network(
|
||||
name_or_id=network_name_or_id, ignore_missing=False)
|
||||
else:
|
||||
self.network = None
|
||||
|
||||
|
||||
def main():
|
||||
|
@ -32,17 +32,18 @@ options:
|
||||
description:
|
||||
- The name or id of the port to which a floating IP is associated.
|
||||
type: str
|
||||
project_id:
|
||||
project:
|
||||
description:
|
||||
- The ID of the project a floating IP is associated with.
|
||||
- The name or ID of the project a floating IP is associated with.
|
||||
type: str
|
||||
aliases: ['project_id']
|
||||
router:
|
||||
description:
|
||||
- The name or id of an associated router.
|
||||
type: str
|
||||
status:
|
||||
description:
|
||||
- The status of a floating IP, which can be ``ACTIVE``or ``DOWN``.
|
||||
- The status of a floating IP.
|
||||
choices: ['active', 'down']
|
||||
type: str
|
||||
requirements:
|
||||
@ -56,8 +57,9 @@ extends_documentation_fragment:
|
||||
RETURN = '''
|
||||
floating_ips:
|
||||
description: The floating ip objects list.
|
||||
type: complex
|
||||
returned: On Success.
|
||||
type: list
|
||||
elements: dict
|
||||
returned: success
|
||||
contains:
|
||||
created_at:
|
||||
description: Timestamp at which the floating IP was assigned.
|
||||
@ -87,9 +89,10 @@ floating_ips:
|
||||
description: Name of the floating ip.
|
||||
type: str
|
||||
port_details:
|
||||
description: The details of the port that this floating IP associates \
|
||||
with. Present if ``fip-port-details`` extension is loaded.
|
||||
type: str
|
||||
description: |
|
||||
The details of the port that this floating IP associates
|
||||
with. Present if C(fip-port-details) extension is loaded.
|
||||
type: dict
|
||||
port_id:
|
||||
description: The port ID floating ip associated with.
|
||||
type: str
|
||||
@ -106,15 +109,16 @@ floating_ips:
|
||||
description: The id of the router floating ip associated with.
|
||||
type: str
|
||||
status:
|
||||
description: The status of a floating IP, which can be ``ACTIVE``or ``DOWN``.\
|
||||
Can be 'ACTIVE' and 'DOWN'.
|
||||
description: |
|
||||
The status of a floating IP, which can be 'ACTIVE' or 'DOWN'.
|
||||
type: str
|
||||
subnet_id:
|
||||
description: The id of the subnet the floating ip associated with.
|
||||
type: str
|
||||
tags:
|
||||
description: List of tags.
|
||||
type: str
|
||||
type: list
|
||||
elements: str
|
||||
updated_at:
|
||||
description: Timestamp at which the floating IP was last updated.
|
||||
type: str
|
||||
@ -146,7 +150,7 @@ class FloatingIPInfoModule(OpenStackModule):
|
||||
floating_ip_address=dict(),
|
||||
floating_network=dict(),
|
||||
port=dict(),
|
||||
project_id=dict(),
|
||||
project=dict(aliases=['project_id']),
|
||||
router=dict(),
|
||||
status=dict(choices=['active', 'down']),
|
||||
)
|
||||
@ -155,46 +159,42 @@ class FloatingIPInfoModule(OpenStackModule):
|
||||
)
|
||||
|
||||
def run(self):
|
||||
query = dict((k, self.params[k])
|
||||
for k in ['description', 'fixed_ip_address',
|
||||
'floating_ip_address']
|
||||
if self.params[k] is not None)
|
||||
|
||||
for k in ['port', 'router']:
|
||||
if self.params[k]:
|
||||
k_id = '{0}_id'.format(k)
|
||||
find_name = 'find_{0}'.format(k)
|
||||
query[k_id] = getattr(self.conn.network, find_name)(
|
||||
name_or_id=self.params[k], ignore_missing=False)['id']
|
||||
|
||||
floating_network_name_or_id = self.params['floating_network']
|
||||
if floating_network_name_or_id:
|
||||
query['floating_network_id'] = self.conn.network.find_network(
|
||||
name_or_id=floating_network_name_or_id,
|
||||
ignore_missing=False)['id']
|
||||
|
||||
project_name_or_id = self.params['project']
|
||||
if project_name_or_id:
|
||||
project = self.conn.identity.find_project(project_name_or_id)
|
||||
if project:
|
||||
query['project_id'] = project['id']
|
||||
else:
|
||||
# caller might not have permission to query projects
|
||||
# so assume she gave a project id
|
||||
query['project_id'] = project_name_or_id
|
||||
|
||||
description = self.params['description']
|
||||
fixed_ip_address = self.params['fixed_ip_address']
|
||||
floating_ip_address = self.params['floating_ip_address']
|
||||
floating_network = self.params['floating_network']
|
||||
port = self.params['port']
|
||||
project_id = self.params['project_id']
|
||||
router = self.params['router']
|
||||
status = self.params['status']
|
||||
|
||||
query = {}
|
||||
if description:
|
||||
query['description'] = description
|
||||
if fixed_ip_address:
|
||||
query['fixed_ip_address'] = fixed_ip_address
|
||||
if floating_ip_address:
|
||||
query['floating_ip_address'] = floating_ip_address
|
||||
if floating_network:
|
||||
try:
|
||||
query['floating_network_id'] = self.conn.network.find_network(name_or_id=floating_network,
|
||||
ignore_missing=False).id
|
||||
except self.sdk.exceptions.ResourceNotFound:
|
||||
self.fail_json(msg="floating_network not found")
|
||||
if port:
|
||||
try:
|
||||
query['port_id'] = self.conn.network.find_port(name_or_id=port, ignore_missing=False).id
|
||||
except self.sdk.exceptions.ResourceNotFound:
|
||||
self.fail_json(msg="port not found")
|
||||
if project_id:
|
||||
query['project_id'] = project_id
|
||||
if router:
|
||||
try:
|
||||
query['router_id'] = self.conn.network.find_router(name_or_id=router, ignore_missing=False).id
|
||||
except self.sdk.exceptions.ResourceNotFound:
|
||||
self.fail_json(msg="router not found")
|
||||
if status:
|
||||
query['status'] = status.upper()
|
||||
|
||||
ips = [ip.to_dict(computed=False) for ip in self.conn.network.ips(**query)]
|
||||
self.exit_json(changed=False, floating_ips=ips)
|
||||
self.exit_json(
|
||||
changed=False,
|
||||
floating_ips=[ip.to_dict(computed=False)
|
||||
for ip in self.conn.network.ips(**query)])
|
||||
|
||||
|
||||
def main():
|
||||
|
Loading…
x
Reference in New Issue
Block a user