Allow to attach multiple floating ips to a server
OpenStack allows to attach multiple floating ips to a single server. Previously, only one floating ip was supported by this module. It would call openstacksdk's get_server_public_ip(), which in turn would return just one of the attached floating ips. If this floating ip would not point to the right nat_destination or fixed_address, then the module would fail. If no floating ip had been attached to a server, then this module would call openstacksdk's add_ips_to_server() with both parameters "floating_ip_address" and "network" to attach a floating ip to the server. But both parameters are mutually exclusive [1], i.e. add_ips_to_server() will ignore "floating_ip_address" if "network" is set and then choose any non-attached floating ip from "network". If "floating_ip_address" has not been created in OpenStack and "network" is not given, then this module would not create this floating ip [2]. The new module code allows to create and add more than one floating ip to a server. It priorizes more specific parameters over generic ones, i.e. if both "floating_ip_address" and "network" are given, then "floating_ip_address" precedes "network". Parameter "network" is now required if "floating_ip_address" is specified, because both are necessary when creating floating ips. Module documentation and args have been updated accordingly. Ref.: [1]a6b0ece282/openstack/cloud/_floating_ip.py (L987)
[2]a6b0ece282/openstack/cloud/_floating_ip.py (L907)
Task: 40939 Story: 2008181 Change-Id: I1ada1be0994f526f72f81f7458782afbcca3c92c
This commit is contained in:
parent
0e370b2c51
commit
3a08a9c07c
466
ci/roles/floating_ip/tasks/main.yml
Normal file
466
ci/roles/floating_ip/tasks/main.yml
Normal file
@ -0,0 +1,466 @@
|
||||
---
|
||||
# Prepare environment
|
||||
- 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.openstack_networks|length == 1
|
||||
|
||||
- name: Create external network
|
||||
openstack.cloud.network:
|
||||
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
|
||||
|
||||
- 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
|
||||
|
||||
- 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
|
||||
|
||||
- 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
|
||||
|
||||
- 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.openstack_networks.0.id)|
|
||||
list|length > 0 }}"
|
||||
|
||||
# TODO: Replace with appropriate Ansible module once available
|
||||
- name: Create a floating ip on public network (required for simplest, first floating ip test)
|
||||
command: openstack --os-cloud={{ cloud }} floating ip create public
|
||||
when: not public_network_had_fips
|
||||
|
||||
# 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 server 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
|
||||
|
||||
- name: Assert one internal port and no floating ips 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.openstack_servers|length == 1
|
||||
- info.openstack_servers.0.public_v4|length == 0
|
||||
- info.openstack_servers.0.public_v6|length == 0
|
||||
- info.openstack_servers.0.addresses.ansible_internal|length == 1
|
||||
- info.openstack_servers.0.addresses.ansible_internal|map(attribute="addr")|sort|list == ["10.7.7.100"]
|
||||
|
||||
- name: Create server 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
|
||||
|
||||
- name: Assert two internal ports and no floating ips on server 2
|
||||
assert:
|
||||
that:
|
||||
- info.openstack_servers|length == 1
|
||||
- info.openstack_servers.0.public_v4|length == 0
|
||||
- info.openstack_servers.0.public_v6|length == 0
|
||||
- info.openstack_servers.0.addresses.ansible_internal|length == 2
|
||||
- info.openstack_servers.0.addresses.ansible_internal|map(attribute="addr")|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 }}"
|
||||
server: ansible_server1
|
||||
register: info
|
||||
|
||||
- name: Assert one internal port and one floating ip on server 1
|
||||
assert:
|
||||
that:
|
||||
- info.openstack_servers.0.addresses.ansible_internal|length == 2
|
||||
- info.openstack_servers.0.addresses.ansible_internal|map(attribute="OS-EXT-IPS:type")|sort|list ==
|
||||
["fixed", "floating"]
|
||||
|
||||
- name: Detach floating IP from server
|
||||
openstack.cloud.floating_ip:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
server: ansible_server1
|
||||
network: public
|
||||
floating_ip_address: "{{ (info.openstack_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 }}"
|
||||
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.openstack_servers.0.addresses.ansible_internal, so retry if necessary.
|
||||
retries: 10
|
||||
delay: 3
|
||||
until: info.openstack_servers.0.addresses.ansible_internal|length == 1
|
||||
|
||||
- name: Assert one internal port on server 1
|
||||
assert:
|
||||
that:
|
||||
- info.openstack_servers.0.addresses.ansible_internal|length == 1
|
||||
- info.openstack_servers.0.addresses.ansible_internal|map(attribute="addr")|list == ["10.7.7.100"]
|
||||
|
||||
- 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: Get info about server
|
||||
openstack.cloud.server_info:
|
||||
cloud: "{{ cloud }}"
|
||||
server: ansible_server2
|
||||
register: info
|
||||
|
||||
- name: Assert two internal ports and one floating ip on server 2
|
||||
assert:
|
||||
that:
|
||||
- info.openstack_servers.0.addresses.ansible_internal|length == 3
|
||||
- info.openstack_servers.0.addresses.ansible_internal|map(attribute="OS-EXT-IPS:type")|sort|list ==
|
||||
["fixed", "fixed", "floating"]
|
||||
|
||||
- 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"
|
||||
|
||||
# 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: Get info about server
|
||||
openstack.cloud.server_info:
|
||||
cloud: "{{ cloud }}"
|
||||
server: ansible_server2
|
||||
register: info
|
||||
# retry because we cannot wait for second floating ip
|
||||
retries: 10
|
||||
delay: 3
|
||||
until: info.openstack_servers.0.addresses.ansible_internal|length == 4
|
||||
|
||||
- name: Assert two internal ports and two floating ips on server 2
|
||||
assert:
|
||||
that:
|
||||
- info.openstack_servers.0.addresses.ansible_internal|length == 4
|
||||
- ("10.6.6.150" in info.openstack_servers.0.addresses.ansible_internal|map(attribute="addr")|sort|list)
|
||||
|
||||
- name: Detach second floating IP from server
|
||||
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 }}"
|
||||
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.openstack_servers.0.addresses.ansible_internal, so retry if necessary.
|
||||
retries: 10
|
||||
delay: 3
|
||||
until: info.openstack_servers.0.addresses.ansible_internal|length == 3
|
||||
|
||||
- name: Assert two internal ports and one floating ip on server 2
|
||||
assert:
|
||||
that:
|
||||
- info.openstack_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.openstack_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 }}"
|
||||
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.openstack_servers.0.addresses.ansible_internal, so retry if necessary.
|
||||
retries: 10
|
||||
delay: 3
|
||||
until: info.openstack_servers.0.addresses.ansible_internal|length == 2
|
||||
|
||||
- name: Assert two internal ports on server 2
|
||||
assert:
|
||||
that:
|
||||
- info.openstack_servers.0.addresses.ansible_internal|length == 2
|
||||
- info.openstack_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
|
||||
|
||||
- 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
|
||||
|
||||
# TODO: Replace with appropriate Ansible module once available
|
||||
- name: Delete floating ip on public network if we created it
|
||||
when: not public_network_had_fips
|
||||
command: >
|
||||
openstack --os-cloud={{ cloud }} floating ip delete
|
||||
{{ fips.floating_ips|selectattr('floating_network_id', '==', public_network.openstack_networks.0.id)|
|
||||
map(attribute="floating_ip_address")|list|join(' ') }}
|
||||
|
||||
# TODO: Replace with appropriate Ansible module once available
|
||||
- name: Delete floating ip 1
|
||||
command: openstack --os-cloud={{ cloud }} floating ip delete 10.6.6.150
|
||||
when: fips.floating_ips|length > 0 and "10.6.6.150" in fips.floating_ips|map(attribute="floating_ip_address")|list
|
||||
|
||||
- name: Get remaining floating ips on external network
|
||||
openstack.cloud.floating_ip_info:
|
||||
cloud: "{{ cloud }}"
|
||||
floating_network: ansible_external
|
||||
register: fips
|
||||
|
||||
# TODO: Replace with appropriate Ansible module once available
|
||||
# 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
|
||||
command: >
|
||||
openstack --os-cloud={{ cloud }} floating ip delete
|
||||
{{ fips.floating_ips|map(attribute="floating_ip_address")|list|join(' ') }}
|
||||
|
||||
# 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 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
|
@ -50,3 +50,4 @@
|
||||
when: sdk_version is version("0.53.0", '>=')
|
||||
- role: loadbalancer
|
||||
tags: loadbalancer
|
||||
- { role: floating_ip, tags: floating_ip }
|
||||
|
@ -11,6 +11,7 @@ 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.
|
||||
options:
|
||||
server:
|
||||
description:
|
||||
@ -24,9 +25,9 @@ options:
|
||||
type: str
|
||||
floating_ip_address:
|
||||
description:
|
||||
- A floating IP address to attach or to detach. Required only if I(state)
|
||||
is absent. When I(state) is present can be used to specify a IP address
|
||||
to attach.
|
||||
- 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:
|
||||
@ -49,7 +50,7 @@ options:
|
||||
wait:
|
||||
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.
|
||||
- Must be set to C(yes) for the module to return the value of the floating IP when attaching.
|
||||
type: bool
|
||||
default: 'no'
|
||||
timeout:
|
||||
@ -118,8 +119,8 @@ EXAMPLES = '''
|
||||
server: cattle001
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import remove_values
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||
import itertools
|
||||
|
||||
|
||||
class NetworkingFloatingIPModule(OpenStackModule):
|
||||
@ -137,16 +138,57 @@ class NetworkingFloatingIPModule(OpenStackModule):
|
||||
purge=dict(required=False, type='bool', default=False),
|
||||
)
|
||||
|
||||
module_kwargs = dict()
|
||||
module_kwargs = dict(
|
||||
required_if=[
|
||||
['state', 'absent', ['floating_ip_address']]
|
||||
],
|
||||
required_by=dict(
|
||||
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})
|
||||
|
||||
if not f_ips:
|
||||
return None
|
||||
|
||||
return f_ips[0]
|
||||
|
||||
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
|
||||
])
|
||||
|
||||
def _match_floating_ip(self, server,
|
||||
floating_ip_address,
|
||||
network_id,
|
||||
fixed_address,
|
||||
nat_destination):
|
||||
|
||||
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)
|
||||
|
||||
# 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)
|
||||
|
||||
def run(self):
|
||||
server_name_or_id = self.params['server']
|
||||
state = self.params['state']
|
||||
@ -160,83 +202,96 @@ class NetworkingFloatingIPModule(OpenStackModule):
|
||||
purge = self.params['purge']
|
||||
|
||||
server = self.conn.get_server(server_name_or_id)
|
||||
if server is None:
|
||||
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 f_ip already assigned to server, check that it matches
|
||||
# requirements.
|
||||
public_ip = self.conn.get_server_public_ip(server)
|
||||
f_ip = self._get_floating_ip(public_ip) if public_ip else public_ip
|
||||
if f_ip:
|
||||
if network:
|
||||
network_id = self.conn.get_network(name_or_id=network)["id"]
|
||||
else:
|
||||
network_id = None
|
||||
# check if we have floating ip on given nat_destination network
|
||||
if nat_destination:
|
||||
nat_floating_addrs = [
|
||||
addr for addr in server.addresses.get(
|
||||
self.conn.get_network(nat_destination)['name'], [])
|
||||
if addr['addr'] == public_ip
|
||||
and addr['OS-EXT-IPS:type'] == 'floating'
|
||||
]
|
||||
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 len(nat_floating_addrs) == 0:
|
||||
self.fail_json(
|
||||
msg="server {server} already has a "
|
||||
"floating-ip on a different "
|
||||
"nat-destination than '{nat_destination}'"
|
||||
.format(server=server_name_or_id,
|
||||
nat_destination=nat_destination))
|
||||
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 all([fixed_address, f_ip.fixed_ip_address == fixed_address,
|
||||
network, f_ip.network != network_id]):
|
||||
# Current state definitely conflicts with requirements
|
||||
self.fail_json(
|
||||
msg="server {server} already has a "
|
||||
"floating-ip on requested "
|
||||
"interface but it doesn't match "
|
||||
"requested network {network}: {fip}"
|
||||
.format(server=server_name_or_id,
|
||||
network=network,
|
||||
fip=remove_values(f_ip, self.no_log_values)))
|
||||
if not network or f_ip.network == network_id:
|
||||
# Requirements are met
|
||||
self.exit_json(changed=False, floating_ip=f_ip)
|
||||
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.
|
||||
|
||||
# Requirements are vague enough to ignore existing f_ip and try
|
||||
# to create a new f_ip to the server.
|
||||
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,
|
||||
reuse=reuse, fixed_address=fixed_address, wait=wait,
|
||||
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)
|
||||
fip_address = self.conn.get_server_public_ip(server)
|
||||
# Update the floating IP status
|
||||
f_ip = self._get_floating_ip(fip_address)
|
||||
|
||||
# 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':
|
||||
if floating_ip_address is None:
|
||||
if not server_name_or_id:
|
||||
self.fail_json(msg="either server or floating_ip_address are required")
|
||||
server = self.conn.get_server(server_name_or_id)
|
||||
floating_ip_address = self.conn.get_server_public_ip(server)
|
||||
|
||||
f_ip = self._get_floating_ip(floating_ip_address)
|
||||
|
||||
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'])
|
||||
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'])
|
||||
changed = True
|
||||
|
||||
if purge:
|
||||
self.conn.delete_floating_ip(f_ip['id'])
|
||||
self.exit_json(changed=True)
|
||||
|
Loading…
x
Reference in New Issue
Block a user