Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hcloud_network module #59366

Merged
merged 7 commits into from
Jul 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
225 changes: 225 additions & 0 deletions lib/ansible/modules/cloud/hcloud/hcloud_network.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
#!/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_network

short_description: Create and manage cloud Networks on the Hetzner Cloud.
LKaemmerling marked this conversation as resolved.
Show resolved Hide resolved

version_added: "2.9"

description:
- Create, update and manage cloud Networks on the Hetzner Cloud.
- You need at least hcloud-python 1.3.0.

author:
- Lukas Kaemmerling (@lkaemmerling)

options:
id:
description:
- The ID of the Hetzner Cloud Networks to manage.
- Only required if no Network I(name) is given.
type: int
name:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I understand it correctly that the network is not updated / recreated if name changes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name should not be changeable, because it is the only (user-controllable) factor.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe then make sure that the module fails in case a network is identified by ID (and exists), but the name is also specified and does not match. Otherwise users might get confused why the module isn't renaming the network.

description:
- The Name of the Hetzner Cloud Network to manage.
- Only required if no Network I(id) is given or a Network does not exists.
type: str
ip_range:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I understand it correctly that the network is not updated / recreated if ip_range changes?

description:
- IP range of the Network.
- Required if Network does not exists.
type: str
labels:
description:
- User-defined labels (key-value pairs).
type: dict
state:
description:
- State of the Network.
default: present
choices: [ absent, present ]
type: str

requirements:
- hcloud-python >= 1.3.0

extends_documentation_fragment: hcloud
"""

EXAMPLES = """
- name: Create a basic network
hcloud_network:
name: my-network
ip_range: 10.0.0.0/8
state: present

- name: Ensure the Network is absent (remove if needed)
hcloud_network:
name: my-network
state: absent
"""

RETURN = """
hcloud_network:
description: The Network
returned: always
type: complex
contains:
id:
description: ID of the Network
type: int
returned: always
sample: 12345
name:
description: Name of the Network
type: string
returned: always
sample: my-volume
ip_range:
description: IP range of the Network
type: str
returned: always
sample: 10.0.0.0/8
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 AnsibleHcloudNetwork(Hcloud):
def __init__(self, module):
super(AnsibleHcloudNetwork, self).__init__(module, "hcloud_network")
self.hcloud_network = None

def _prepare_result(self):
return {
"id": to_native(self.hcloud_network.id),
"name": to_native(self.hcloud_network.name),
"ip_range": to_native(self.hcloud_network.ip_range),
"labels": self.hcloud_network.labels,
}

def _get_network(self):
try:
if self.module.params.get("id") is not None:
self.hcloud_network = self.client.networks.get_by_id(
self.module.params.get("id")
)
else:
self.hcloud_network = self.client.networks.get_by_name(
self.module.params.get("name")
)
except APIException as e:
self.module.fail_json(msg=e.message)

def _create_network(self):

self.module.fail_on_missing_params(
required_params=["name", "ip_range"]
)
params = {
"name": self.module.params.get("name"),
"ip_range": self.module.params.get("ip_range"),
"labels": self.module.params.get("labels"),
}

if not self.module.check_mode:
self.client.networks.create(**params)

self._mark_as_changed()
self._get_network()

def _update_network(self):

labels = self.module.params.get("labels")
if labels is not None and labels != self.hcloud_network.labels:
if not self.module.check_mode:
self.hcloud_network.update(labels=labels)
self._mark_as_changed()

ip_range = self.module.params.get("ip_range")
if ip_range is not None and ip_range != self.hcloud_network.ip_range:
if not self.module.check_mode:
self.hcloud_network.change_ip_range(ip_range=ip_range).wait_until_finished()
self._mark_as_changed()

self._get_network()

def present_network(self):
self._get_network()
if self.hcloud_network is None:
self._create_network()
else:
self._update_network()

def delete_network(self):
self._get_network()
if self.hcloud_network is not None:
if not self.module.check_mode:
self.client.networks.delete(self.hcloud_network)
self._mark_as_changed()
self.hcloud_network = None

@staticmethod
def define_module():
return AnsibleModule(
argument_spec=dict(
id={"type": "int"},
name={"type": "str"},
ip_range={"type": "str"},
labels={"type": "dict"},
state={
"choices": ["absent", "present"],
"default": "present",
},
**Hcloud.base_module_arguments()
),
required_one_of=[['id', 'name']],
supports_check_mode=True,
)


def main():
module = AnsibleHcloudNetwork.define_module()

hcloud = AnsibleHcloudNetwork(module)
state = module.params["state"]
if state == "absent":
hcloud.delete_network()
elif state == "present":
hcloud.present_network()

module.exit_json(**hcloud.get_result())


if __name__ == "__main__":
main()
2 changes: 2 additions & 0 deletions test/integration/targets/hcloud_network/aliases
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cloud/hcloud
shippable/hcloud/group1
5 changes: 5 additions & 0 deletions test/integration/targets/hcloud_network/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# 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_network_name: "{{hcloud_prefix}}-integ"
110 changes: 110 additions & 0 deletions test/integration/targets/hcloud_network/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# 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
hcloud_network:
name: "{{ hcloud_network_name }}"
state: absent
register: result
- name: verify setup
assert:
that:
- result is success

- name: test missing ip_range parameter on create Network
hcloud_network:
name: "{{hcloud_network_name}}"
register: result
ignore_errors: yes
- name: verify fail missing ip_range parameter on create Network result
assert:
that:
- result is failed
- 'result.msg == "missing required arguments: ip_range"'

- name: test create Network with check mode
hcloud_network:
name: "{{hcloud_network_name}}"
ip_range: "10.0.0.0/16"
register: result
check_mode: yes
- name: verify create Network with check mode result
assert:
that:
- result is changed

- name: test create Network
hcloud_network:
name: "{{hcloud_network_name}}"
ip_range: "10.0.0.0/16"
register: network
- name: verify test create Network result
assert:
that:
- network is changed
- network.hcloud_network.name == "{{hcloud_network_name}}"
- network.hcloud_network.ip_range == "10.0.0.0/16"

- name: test create Network idempotence
hcloud_network:
name: "{{hcloud_network_name}}"
ip_range: "10.0.0.0/16"
register: network
- name: verify test create network
assert:
that:
- network is not changed

- name: test update Network label
hcloud_network:
name: "{{hcloud_network_name}}"
labels:
key: value
register: network
- name: verify test update Network label
assert:
that:
- network is changed
- network.hcloud_network.labels.key == "value"

- name: test update Network label idempotency
hcloud_network:
name: "{{hcloud_network_name}}"
labels:
key: value
register: network
- name: verify test update Network label idempotency
assert:
that:
- network is not changed

- name: test update Network ip range
hcloud_network:
name: "{{hcloud_network_name}}"
ip_range: "10.0.0.0/8"
register: network
- name: verify test update Network ip range
assert:
that:
- network is changed
- network.hcloud_network.ip_range == "10.0.0.0/8"

- name: test update Network ip range idempotency
hcloud_network:
name: "{{hcloud_network_name}}"
ip_range: "10.0.0.0/8"
register: network
- name: verify test update Network ip range idempotency
assert:
that:
- network is not changed

- name: test delete Network
LKaemmerling marked this conversation as resolved.
Show resolved Hide resolved
hcloud_network:
name: "{{hcloud_network_name}}"
state: absent
register: result
- name: verify delete Network
assert:
that:
- result is success
2 changes: 1 addition & 1 deletion test/runner/requirements/integration.cloud.hcloud.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
hcloud ; python_version >= '2.7' and python_version <= '3.7' # Python 2.6 and 3.8 are not supported
hcloud>=1.3.0 ; python_version >= '2.7' # Python 2.6 is not supported (sanity_ok); Only hcloud >= 1.3.0 supports Networks.