Add hcloud_floating_ip module (#62409)

This commit is contained in:
Lukas Kämmerling 2019-09-18 11:26:53 +02:00 committed by René Moser
parent 24ff3f257b
commit fd7e156ccd
7 changed files with 667 additions and 5 deletions

View file

@ -0,0 +1,328 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.1",
"status": ["preview"],
"supported_by": "community",
}
DOCUMENTATION = """
---
module: hcloud_floating_ip
short_description: Create and manage cloud Floating IPs on the Hetzner Cloud.
version_added: "2.10"
description:
- Create, update and manage cloud Floating IPs on the Hetzner Cloud.
author:
- Lukas Kaemmerling (@lkaemmerling)
options:
id:
description:
- The ID of the Hetzner Cloud Floating IPs to manage.
- Only required if no Floating IP I(name) is given.
type: int
name:
description:
- The Name of the Hetzner Cloud Floating IPs to manage.
- Only required if no Floating IP I(id) is given or a Floating IP does not exists.
type: str
description:
description:
- The Description of the Hetzner Cloud Floating IPs.
type: str
home_location:
description:
- Home Location of the Hetzner Cloud Floating IP.
- Required if no I(server) is given and Floating IP does not exists.
type: str
server:
description:
- Server Name the Floating IP should be assigned to.
- Required if no I(home_location) is given and Floating IP does not exists.
type: str
type:
description:
- Type of the Floating IP.
- Required if Floating IP does not exists
choices: [ ipv4, ipv6 ]
type: str
force:
description:
- Force the assignment or deletion of the Floating IP.
type: bool
labels:
description:
- User-defined labels (key-value pairs).
type: dict
state:
description:
- State of the Floating IP.
default: present
choices: [ absent, present ]
type: str
requirements:
- hcloud-python >= 1.6.0
extends_documentation_fragment: hcloud
"""
EXAMPLES = """
- name: Create a basic IPv4 Floating IP
hcloud_floating_ip:
name: my-floating-ip
home_location: fsn1
type: ipv4
state: present
- name: Create a basic IPv6 Floating IP
hcloud_floating_ip:
name: my-floating-ip
home_location: fsn1
type: ipv6
state: present
- name: Assign a Floating IP to a server
hcloud_floating_ip:
name: my-floating-ip
server: 1234
state: present
- name: Assign a Floating IP to another server
hcloud_floating_ip:
name: my-floating-ip
server: 1234
force: yes
state: present
- name: Floating IP should be absent
hcloud_floating_ip:
name: my-floating-ip
state: absent
"""
RETURN = """
hcloud_floating_ip:
description: The Floating IP instance
returned: Always
type: complex
contains:
id:
description: ID of the Floating IP
type: int
returned: Always
sample: 12345
name:
description: Name of the Floating IP
type: string
returned: Always
sample: my-floating-ip
description:
description: Description of the Floating IP
type: string
returned: Always
sample: my-floating-ip
ip:
description: IP Address of the Floating IP
type: string
returned: Always
sample: 116.203.104.109
type:
description: Type of the Floating IP
type: string
returned: Always
sample: ipv4
home_location:
description: Name of the home location of the Floating IP
type: string
returned: Always
sample: fsn1
server:
description: Name of the server the Floating IP is assigned to.
type: string
returned: Always
sample: "my-server"
labels:
description: User-defined labels (key-value pairs)
type: dict
returned: Always
sample:
key: value
mylabel: 123
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
from ansible.module_utils.hcloud import Hcloud
try:
from hcloud import APIException
except ImportError:
APIException = None
class AnsibleHcloudFloatingIP(Hcloud):
def __init__(self, module):
super(AnsibleHcloudFloatingIP, self).__init__(module, "hcloud_floating_ip")
self.hcloud_floating_ip = None
def _prepare_result(self):
server = None
if self.hcloud_floating_ip.server is not None:
server = to_native(self.hcloud_floating_ip.server.name)
return {
"id": to_native(self.hcloud_floating_ip.id),
"name": to_native(self.hcloud_floating_ip.name),
"description": to_native(self.hcloud_floating_ip.description),
"ip": to_native(self.hcloud_floating_ip.ip),
"type": to_native(self.hcloud_floating_ip.type),
"home_location": to_native(self.hcloud_floating_ip.home_location.name),
"labels": self.hcloud_floating_ip.labels,
"server": server,
}
def _get_floating_ip(self):
try:
if self.module.params.get("id") is not None:
self.hcloud_floating_ip = self.client.floating_ips.get_by_id(
self.module.params.get("id")
)
else:
self.hcloud_floating_ip = self.client.floating_ips.get_by_name(
self.module.params.get("name")
)
except APIException as e:
self.module.fail_json(msg=e.message)
def _create_floating_ip(self):
self.module.fail_on_missing_params(
required_params=["type"]
)
params = {
"description": self.module.params.get("description"),
"type": self.module.params.get("type"),
"name": self.module.params.get("name"),
}
if self.module.params.get("home_location") is not None:
params["home_location"] = self.client.locations.get_by_name(
self.module.params.get("home_location")
)
elif self.module.params.get("server") is not None:
params["server"] = self.client.servers.get_by_name(
self.module.params.get("server")
)
else:
self.module.fail_json(msg="one of the following is required: home_location, server")
if self.module.params.get("labels") is not None:
params["labels"] = self.module.params.get("labels")
if not self.module.check_mode:
resp = self.client.floating_ips.create(**params)
self.hcloud_floating_ip = resp.floating_ip
self._mark_as_changed()
self._get_floating_ip()
def _update_floating_ip(self):
labels = self.module.params.get("labels")
if labels is not None and labels != self.hcloud_floating_ip.labels:
if not self.module.check_mode:
self.hcloud_floating_ip.update(labels=labels)
self._mark_as_changed()
description = self.module.params.get("description")
if description is not None and description != self.hcloud_floating_ip.description:
if not self.module.check_mode:
self.hcloud_floating_ip.update(description=description)
self._mark_as_changed()
server = self.module.params.get("server")
if server is not None:
if self.module.params.get("force") or self.hcloud_floating_ip.server is None:
if not self.module.check_mode:
self.hcloud_floating_ip.assign(
self.client.servers.get_by_name(self.module.params.get("server"))
)
else:
self.module.warn(
"Floating IP is already assigned to server %s. You need to unassign the Floating IP or use force=yes."
% self.hcloud_floating_ip.server.name
)
self._mark_as_changed()
elif server is None and self.hcloud_floating_ip.server is not None:
if not self.module.check_mode:
self.hcloud_floating_ip.unassign()
self._mark_as_changed()
self._get_floating_ip()
def present_floating_ip(self):
self._get_floating_ip()
if self.hcloud_floating_ip is None:
self._create_floating_ip()
else:
self._update_floating_ip()
def delete_floating_ip(self):
self._get_floating_ip()
if self.hcloud_floating_ip is not None:
if self.module.params.get("force") or self.hcloud_floating_ip.server is None:
if not self.module.check_mode:
self.client.floating_ips.delete(self.hcloud_floating_ip)
else:
self.module.warn(
"Floating IP is currently assigned to server %s. You need to unassign the Floating IP or use force=yes."
% self.hcloud_floating_ip.server.name
)
self._mark_as_changed()
self.hcloud_floating_ip = None
@staticmethod
def define_module():
return AnsibleModule(
argument_spec=dict(
id={"type": "int"},
name={"type": "str"},
description={"type": "str"},
server={"type": "str"},
home_location={"type": "str"},
force={"type": "bool"},
type={"choices": ["ipv4", "ipv6"]},
labels={"type": "dict"},
state={
"choices": ["absent", "present"],
"default": "present",
},
**Hcloud.base_module_arguments()
),
required_one_of=[['id', 'name']],
mutually_exclusive=[['home_location', 'server']],
supports_check_mode=True,
)
def main():
module = AnsibleHcloudFloatingIP.define_module()
hcloud = AnsibleHcloudFloatingIP(module)
state = module.params["state"]
if state == "absent":
hcloud.delete_floating_ip()
elif state == "present":
hcloud.present_floating_ip()
module.exit_json(**hcloud.get_result())
if __name__ == "__main__":
main()

View file

@ -0,0 +1,2 @@
cloud/hcloud
shippable/hcloud/group1

View file

@ -0,0 +1,6 @@
# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
---
hcloud_prefix: "tests"
hcloud_floating_ip_name: "{{hcloud_prefix}}-integration"
hcloud_server_name: "{{hcloud_prefix}}-fip-tests"

View file

@ -0,0 +1,325 @@
# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
---
- name: setup server
hcloud_server:
name: "{{ hcloud_server_name }}"
server_type: cx11
image: ubuntu-18.04
state: started
location: "fsn1"
register: main_server
- name: verify setup server
assert:
that:
- main_server is changed
- name: setup another server
hcloud_server:
name: "{{ hcloud_server_name }}2"
server_type: cx11
image: ubuntu-18.04
state: started
register: main_server2
- name: verify setup another server
assert:
that:
- main_server2 is changed
- name: test missing type parameter on create Floating IP
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
register: result
ignore_errors: yes
- name: verify fail test missing type parameter on create Floating IP
assert:
that:
- result is failed
- 'result.msg == "missing required arguments: type"'
- name: test missing required parameters on create Floating IP
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
type: ipv4
register: result
ignore_errors: yes
- name: verify fail test missing required parameters on create Floating IP
assert:
that:
- result is failed
- 'result.msg == "one of the following is required: home_location, server"'
- name: test missing type parameter on delete Floating IP
hcloud_floating_ip:
type: ipv4
home_location: "fsn1"
state: "absent"
register: result
ignore_errors: yes
- name: verify fail test missing type parameter on delete Floating IP
assert:
that:
- result is failed
- 'result.msg == "one of the following is required: id, name"'
- name: test create Floating IP with check mode
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
description: "Web Server"
type: ipv4
home_location: "fsn1"
register: floatingIP
check_mode: yes
- name: verify test create Floating IP with check mode
assert:
that:
- floatingIP is changed
- name: test create Floating IP
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
description: "Web Server"
type: ipv4
home_location: "fsn1"
register: floatingIP
- name: verify test create Floating IP
assert:
that:
- floatingIP is changed
- floatingIP.hcloud_floating_ip.name ==hcloud_floating_ip_name
- floatingIP.hcloud_floating_ip.description == "Web Server"
- floatingIP.hcloud_floating_ip.home_location == "fsn1"
- name: test create Floating IP idempotency
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
description: "Web Server"
type: ipv4
home_location: "fsn1"
register: floatingIP
- name: verify test create Floating IP idempotency
assert:
that:
- floatingIP is not changed
- name: test update Floating IP with check mode
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
description: "changed-description"
type: ipv4
home_location: "fsn1"
check_mode: yes
register: floatingIP
- name: verify test create Floating IP with check mode
assert:
that:
- floatingIP is changed
- floatingIP.hcloud_floating_ip.description == "Web Server"
- name: test update Floating IP
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
description: "changed-description"
type: ipv4
home_location: "fsn1"
labels:
key: value
register: floatingIP
- name: verify test update Floating IP
assert:
that:
- floatingIP is changed
- floatingIP.hcloud_floating_ip.description == "changed-description"
- name: test update Floating IP idempotency
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
description: "changed-description"
type: ipv4
home_location: "fsn1"
labels:
key: value
register: floatingIP
- name: verify test update Floating IP idempotency
assert:
that:
- floatingIP is not changed
- name: test update Floating IP with same labels
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
type: ipv4
home_location: "fsn1"
labels:
key: value
register: floatingIP
- name: verify test update Floating IP with same labels
assert:
that:
- floatingIP is not changed
- name: test update Floating IP with other labels
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
type: ipv4
home_location: "fsn1"
labels:
key: value
other: label
register: floatingIP
- name: verify test update Floating IP with other labels
assert:
that:
- floatingIP is changed
- name: test update Floating IP with other labels in different order
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
type: ipv4
home_location: "fsn1"
labels:
other: label
key: value
register: floatingIP
- name: verify test update Floating IP with other labels in different order
assert:
that:
- floatingIP is not changed
- name: test assign Floating IP with checkmode
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
description: "changed-description"
type: ipv4
server: "{{ main_server.hcloud_server.name }}"
check_mode: yes
register: floatingIP
- name: verify test assign Floating IP with checkmode
assert:
that:
- floatingIP is changed
- floatingIP.hcloud_floating_ip.server != "{{ main_server.hcloud_server.name }}"
- name: test assign Floating IP
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
description: "changed-description"
type: ipv4
server: "{{ main_server.hcloud_server.name }}"
register: floatingIP
- name: verify test assign Floating IP
assert:
that:
- floatingIP is changed
- floatingIP.hcloud_floating_ip.server == "{{ main_server.hcloud_server.name }}"
- name: test unassign Floating IP
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
type: ipv4
home_location: "fsn1"
register: floatingIP
- name: verify test unassign Floating IP
assert:
that:
- floatingIP is changed
- floatingIP.hcloud_floating_ip.server != "{{ main_server.hcloud_server.name }}"
- name: test unassign Floating IP idempotency
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
type: ipv4
home_location: "fsn1"
register: floatingIP
- name: verify test unassign Floating IPidempotency
assert:
that:
- floatingIP is not changed
- name: test assign Floating IP again
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
type: ipv4
server: "{{ main_server.hcloud_server.name }}"
register: floatingIP
- name: verify test assign Floating IP again
assert:
that:
- floatingIP is changed
- floatingIP.hcloud_floating_ip.server == "{{ main_server.hcloud_server.name }}"
- name: test already assigned Floating IP assign without force
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
type: ipv4
server: "{{ main_server2.hcloud_server.name }}"
register: floatingIP
- name: verify test already assigned Floating IP assign without force
assert:
that:
- floatingIP is changed
- floatingIP.hcloud_floating_ip.server == "{{ main_server.hcloud_server.name }}"
- name: test already assigned Floating IP assign with force
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
type: ipv4
force: yes
server: "{{ main_server2.hcloud_server.name }}"
register: floatingIP
- name: verify test already assigned Floating IP assign with force
assert:
that:
- floatingIP is changed
- floatingIP.hcloud_floating_ip.server == "{{ main_server2.hcloud_server.name }}"
- name: test delete floating ip
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
state: "absent"
register: result
- name: verify test delete floating ip
assert:
that:
- result is changed
- name: test create ipv6 floating ip
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
type: ipv6
home_location: "fsn1"
state: "present"
register: result
- name: verify test create ipv6 floating ip
assert:
that:
- result is changed
- name: test delete ipv6 floating ip
hcloud_floating_ip:
name: "{{ hcloud_floating_ip_name }}"
state: "absent"
register: result
- name: verify test delete ipv6 floating ip
assert:
that:
- result is changed
- name: cleanup
hcloud_server:
name: "{{ hcloud_server_name }}"
state: absent
register: result
- name: verify cleanup
assert:
that:
- result is changed
- name: cleanup another server
hcloud_server:
name: "{{ main_server2.hcloud_server.name }}"
state: absent
register: result
- name: verify cleanup another server
assert:
that:
- result is changed

View file

@ -7,7 +7,7 @@
- name: verify test gather hcloud floating ip infos in check mode
assert:
that:
- hcloud_floating_ips.hcloud_floating_ip_info| list | count == 1
- hcloud_floating_ips.hcloud_floating_ip_info| list | count >= 1
- name: test gather hcloud floating ip infos in check mode
hcloud_floating_ip_info:
@ -17,7 +17,7 @@
- name: verify test gather hcloud floating ip infos in check mode
assert:
that:
- hcloud_floating_ips.hcloud_floating_ip_info| list | count == 1
- hcloud_floating_ips.hcloud_floating_ip_info| list | count >= 1
- name: test gather hcloud floating ip infos with correct label selector

View file

@ -53,8 +53,7 @@
assert:
that:
- hcloud_network.hcloud_network_info | selectattr('name','equalto','{{ hcloud_network_name }}') | list | count >= 1
- hcloud_network.hcloud_network_info[0].subnetworks | list | count >= 1
- hcloud_network.hcloud_network_info[0].routes | list | count >= 1
- name: test gather hcloud network info with correct label selector
hcloud_network_info:
@ -82,6 +81,8 @@
assert:
that:
- hcloud_network.hcloud_network_info | selectattr('name','equalto','{{ hcloud_network_name }}') | list | count == 1
- hcloud_network.hcloud_network_info[0].subnetworks | list | count >= 1
- hcloud_network.hcloud_network_info[0].routes | list | count >= 1
- name: test gather hcloud network info with wrong name
hcloud_network_info:

View file

@ -1 +1 @@
hcloud>=1.4.1 ; python_version >= '2.7' # Python 2.6 is not supported (sanity_ok); Only hcloud >= 1.3.0 supports Networks; hcloud >= 1.4.1 is needed to allow a wider range of requests versions to be used
hcloud>=1.6.0 ; python_version >= '2.7' # Python 2.6 is not supported (sanity_ok); Only hcloud >= 1.6.0 supports Floating IPs with names