diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-18 05:52:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-18 05:52:35 +0000 |
commit | 7fec0b69a082aaeec72fee0612766aa42f6b1b4d (patch) | |
tree | efb569b86ca4da888717f5433e757145fa322e08 /ansible_collections/hetzner/hcloud/plugins/modules | |
parent | Releasing progress-linux version 7.7.0+dfsg-3~progress7.99u1. (diff) | |
download | ansible-7fec0b69a082aaeec72fee0612766aa42f6b1b4d.tar.xz ansible-7fec0b69a082aaeec72fee0612766aa42f6b1b4d.zip |
Merging upstream version 9.4.0+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/hetzner/hcloud/plugins/modules')
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/certificate.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_certificate.py) | 124 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/certificate_info.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_certificate_info.py) | 80 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/datacenter_info.py | 192 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/firewall.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_firewall.py) | 311 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/firewall_info.py | 246 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/firewall_resource.py | 243 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/floating_ip.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_floating_ip.py) | 109 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/floating_ip_info.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_floating_ip_facts.py) | 101 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/hcloud_datacenter_facts.py | 160 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/hcloud_datacenter_info.py | 160 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/hcloud_floating_ip_info.py | 185 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/hcloud_image_facts.py | 219 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/hcloud_location_facts.py | 159 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_facts.py | 244 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_type_facts.py | 183 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_type_info.py | 183 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/hcloud_ssh_key_info.py | 169 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/hcloud_volume_facts.py | 186 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/image_info.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_image_info.py) | 108 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/iso_info.py | 206 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/load_balancer.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer.py) | 122 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_info.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_info.py) | 128 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_network.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_network.py) | 96 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_service.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_service.py) | 206 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_target.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_target.py) | 168 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_type_info.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_type_info.py) | 79 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/location_info.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_location_info.py) | 86 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/network.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_network.py) | 100 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/network_info.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_network_info.py) | 89 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/placement_group.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_placement_group.py) | 72 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/primary_ip.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_primary_ip.py) | 79 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/primary_ip_info.py | 203 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/rdns.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_rdns.py) | 132 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/route.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_route.py) | 70 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/server.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server.py) | 484 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/server_info.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_info.py) | 120 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/server_network.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_network.py) | 102 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/server_type_info.py | 204 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/ssh_key.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_ssh_key.py) | 87 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/ssh_key_info.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_ssh_key_facts.py) | 95 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/subnetwork.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_subnetwork.py) | 87 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/volume.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_volume.py) | 93 | ||||
-rw-r--r-- | ansible_collections/hetzner/hcloud/plugins/modules/volume_info.py (renamed from ansible_collections/hetzner/hcloud/plugins/modules/hcloud_volume_info.py) | 94 |
43 files changed, 2997 insertions, 3567 deletions
diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_certificate.py b/ansible_collections/hetzner/hcloud/plugins/modules/certificate.py index 0f6dcf0f2..ea39be6ca 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_certificate.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/certificate.py @@ -1,16 +1,14 @@ #!/usr/bin/python -# -*- coding: utf-8 -*- # Copyright: (c) 2020, 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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_certificate +module: certificate short_description: Create and manage certificates on the Hetzner Cloud. @@ -39,17 +37,17 @@ options: certificate: description: - Certificate and chain in PEM format, in order so that each record directly certifies the one preceding. - - Required if certificate does not exist. + - Required if certificate does not exist and I(type=uploaded). type: str private_key: description: - Certificate key in PEM format. - - Required if certificate does not exist. + - Required if certificate does not exist and I(type=uploaded). type: str domain_names: description: - - Certificate key in PEM format. - - Required if certificate does not exist. + - Domains and subdomains that should be contained in the Certificate issued by Let's Encrypt. + - Required if I(type=managed). type: list default: [ ] elements: str @@ -68,28 +66,37 @@ options: extends_documentation_fragment: - hetzner.hcloud.hcloud -''' +""" EXAMPLES = """ - name: Create a basic certificate - hcloud_certificate: + hetzner.hcloud.certificate: name: my-certificate - certificate: "ssh-rsa AAAjjk76kgf...Xt" - private_key: "ssh-rsa AAAjjk76kgf...Xt" + certificate: -----BEGIN CERTIFICATE-----... + private_key: -----BEGIN PRIVATE KEY-----... state: present - name: Create a certificate with labels - hcloud_certificate: + hetzner.hcloud.certificate: name: my-certificate - certificate: "ssh-rsa AAAjjk76kgf...Xt" - private_key: "ssh-rsa AAAjjk76kgf...Xt" + certificate: -----BEGIN CERTIFICATE-----... + private_key: -----BEGIN PRIVATE KEY-----... labels: - key: value - mylabel: 123 + key: value + mylabel: 123 + state: present + +- name: Create a managed certificate + hetzner.hcloud.certificate: + name: my-certificate + type: managed + domain_names: + - example.com + - www.example.com state: present - name: Ensure the certificate is absent (remove if needed) - hcloud_certificate: + hetzner.hcloud.certificate: name: my-certificate state: absent """ @@ -139,14 +146,17 @@ hcloud_certificate: """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.certificates import BoundCertificate -class AnsibleHcloudCertificate(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_certificate") - self.hcloud_certificate = None + +class AnsibleHCloudCertificate(AnsibleHCloud): + represent = "hcloud_certificate" + + hcloud_certificate: BoundCertificate | None = None def _prepare_result(self): return { @@ -158,54 +168,44 @@ class AnsibleHcloudCertificate(Hcloud): "not_valid_before": to_native(self.hcloud_certificate.not_valid_before), "not_valid_after": to_native(self.hcloud_certificate.not_valid_after), "domain_names": [to_native(domain) for domain in self.hcloud_certificate.domain_names], - "labels": self.hcloud_certificate.labels + "labels": self.hcloud_certificate.labels, } def _get_certificate(self): try: if self.module.params.get("id") is not None: - self.hcloud_certificate = self.client.certificates.get_by_id( - self.module.params.get("id") - ) + self.hcloud_certificate = self.client.certificates.get_by_id(self.module.params.get("id")) elif self.module.params.get("name") is not None: - self.hcloud_certificate = self.client.certificates.get_by_name( - self.module.params.get("name") - ) + self.hcloud_certificate = self.client.certificates.get_by_name(self.module.params.get("name")) - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) def _create_certificate(self): - self.module.fail_on_missing_params( - required_params=["name"] - ) + self.module.fail_on_missing_params(required_params=["name"]) params = { "name": self.module.params.get("name"), - "labels": self.module.params.get("labels") + "labels": self.module.params.get("labels"), } - if self.module.params.get('type') == 'uploaded': - self.module.fail_on_missing_params( - required_params=["certificate", "private_key"] - ) + if self.module.params.get("type") == "uploaded": + self.module.fail_on_missing_params(required_params=["certificate", "private_key"]) params["certificate"] = self.module.params.get("certificate") params["private_key"] = self.module.params.get("private_key") if not self.module.check_mode: try: self.client.certificates.create(**params) - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) else: - self.module.fail_on_missing_params( - required_params=["domain_names"] - ) + self.module.fail_on_missing_params(required_params=["domain_names"]) params["domain_names"] = self.module.params.get("domain_names") if not self.module.check_mode: try: resp = self.client.certificates.create_managed(**params) resp.action.wait_until_finished(max_retries=1000) - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self._get_certificate() @@ -214,9 +214,7 @@ class AnsibleHcloudCertificate(Hcloud): try: name = self.module.params.get("name") if name is not None and self.hcloud_certificate.name != name: - self.module.fail_on_missing_params( - required_params=["id"] - ) + self.module.fail_on_missing_params(required_params=["id"]) if not self.module.check_mode: self.hcloud_certificate.update(name=name) self._mark_as_changed() @@ -226,8 +224,8 @@ class AnsibleHcloudCertificate(Hcloud): if not self.module.check_mode: self.hcloud_certificate.update(labels=labels) self._mark_as_changed() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._get_certificate() def present_certificate(self): @@ -243,13 +241,13 @@ class AnsibleHcloudCertificate(Hcloud): if not self.module.check_mode: try: self.client.certificates.delete(self.hcloud_certificate) - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self.hcloud_certificate = None - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, @@ -266,18 +264,18 @@ class AnsibleHcloudCertificate(Hcloud): "choices": ["absent", "present"], "default": "present", }, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), - required_one_of=[['id', 'name']], - required_if=[['state', 'present', ['name']]], + required_one_of=[["id", "name"]], + required_if=[["state", "present", ["name"]]], supports_check_mode=True, ) def main(): - module = AnsibleHcloudCertificate.define_module() + module = AnsibleHCloudCertificate.define_module() - hcloud = AnsibleHcloudCertificate(module) + hcloud = AnsibleHCloudCertificate(module) state = module.params.get("state") if state == "absent": hcloud.delete_certificate() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_certificate_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/certificate_info.py index 855706f1f..e074046fd 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_certificate_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/certificate_info.py @@ -1,16 +1,14 @@ #!/usr/bin/python -# -*- coding: utf-8 -*- # Copyright: (c) 2020, 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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_certificate_info +module: certificate_info short_description: Gather infos about your Hetzner Cloud certificates. description: - Gather facts about your Hetzner Cloud certificates. @@ -20,6 +18,7 @@ options: id: description: - The ID of the certificate you want to get. + - The module will fail if the provided ID is invalid. type: int name: description: @@ -32,11 +31,11 @@ options: extends_documentation_fragment: - hetzner.hcloud.hcloud -''' +""" EXAMPLES = """ - name: Gather hcloud certificate infos - hcloud_certificate_info: + hetzner.hcloud.certificate_info: register: output - name: Print the gathered infos debug: @@ -86,75 +85,76 @@ hcloud_certificate_info: returned: always type: dict """ + from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.certificates import BoundCertificate + +class AnsibleHCloudCertificateInfo(AnsibleHCloud): + represent = "hcloud_certificate_info" -class AnsibleHcloudCertificateInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_certificate_info") - self.hcloud_certificate_info = None + hcloud_certificate_info: list[BoundCertificate] | None = None def _prepare_result(self): certificates = [] for certificate in self.hcloud_certificate_info: if certificate: - certificates.append({ - "id": to_native(certificate.id), - "name": to_native(certificate.name), - "fingerprint": to_native(certificate.fingerprint), - "certificate": to_native(certificate.certificate), - "not_valid_before": to_native(certificate.not_valid_before), - "not_valid_after": to_native(certificate.not_valid_after), - "domain_names": [to_native(domain) for domain in certificate.domain_names], - "labels": certificate.labels - }) + certificates.append( + { + "id": to_native(certificate.id), + "name": to_native(certificate.name), + "fingerprint": to_native(certificate.fingerprint), + "certificate": to_native(certificate.certificate), + "not_valid_before": to_native(certificate.not_valid_before), + "not_valid_after": to_native(certificate.not_valid_after), + "domain_names": [to_native(domain) for domain in certificate.domain_names], + "labels": certificate.labels, + } + ) return certificates def get_certificates(self): try: if self.module.params.get("id") is not None: - self.hcloud_certificate_info = [self.client.certificates.get_by_id( - self.module.params.get("id") - )] + self.hcloud_certificate_info = [self.client.certificates.get_by_id(self.module.params.get("id"))] elif self.module.params.get("name") is not None: - self.hcloud_certificate_info = [self.client.certificates.get_by_name( - self.module.params.get("name") - )] + self.hcloud_certificate_info = [self.client.certificates.get_by_name(self.module.params.get("name"))] elif self.module.params.get("label_selector") is not None: self.hcloud_certificate_info = self.client.certificates.get_all( - label_selector=self.module.params.get("label_selector")) + label_selector=self.module.params.get("label_selector") + ) else: self.hcloud_certificate_info = self.client.certificates.get_all() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, name={"type": "str"}, label_selector={"type": "str"}, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), supports_check_mode=True, ) def main(): - module = AnsibleHcloudCertificateInfo.define_module() + module = AnsibleHCloudCertificateInfo.define_module() + hcloud = AnsibleHCloudCertificateInfo(module) - hcloud = AnsibleHcloudCertificateInfo(module) hcloud.get_certificates() result = hcloud.get_result() - ansible_info = { - 'hcloud_certificate_info': result['hcloud_certificate_info'] - } + ansible_info = {"hcloud_certificate_info": result["hcloud_certificate_info"]} module.exit_json(**ansible_info) diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/datacenter_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/datacenter_info.py new file mode 100644 index 000000000..f6665a6fb --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/datacenter_info.py @@ -0,0 +1,192 @@ +#!/usr/bin/python + +# 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 annotations + +DOCUMENTATION = """ +--- +module: datacenter_info + +short_description: Gather info about the Hetzner Cloud datacenters. + +description: + - Gather info about your Hetzner Cloud datacenters. + +author: + - Lukas Kaemmerling (@LKaemmerling) + +options: + id: + description: + - The ID of the datacenter you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name of the datacenter you want to get. + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Gather hcloud datacenter info + hetzner.hcloud.datacenter_info: + register: output + +- name: Print the gathered info + debug: + var: output + +- name: List available server_types in a datacenter + block: + - name: Gather a hcloud datacenter + hetzner.hcloud.datacenter_info: + name: fsn1-dc14 + register: output + + - name: Gather a hcloud datacenter available server_types + hetzner.hcloud.server_type_info: + id: "{{ item }}" + loop: "{{ output.hcloud_datacenter_info[0].server_types.available }}" + register: available_server_types + + - name: Print a hcloud datacenter available server_types + ansible.builtin.debug: + var: available_server_types.results | map(attribute='hcloud_server_type_info') +""" + +RETURN = """ +hcloud_datacenter_info: + description: + - The datacenter info as list + returned: always + type: complex + contains: + id: + description: Numeric identifier of the datacenter + returned: always + type: int + sample: 1937415 + name: + description: Name of the datacenter + returned: always + type: str + sample: fsn1-dc8 + description: + description: Detail description of the datacenter + returned: always + type: str + sample: Falkenstein DC 8 + location: + description: Name of the location where the datacenter resides in + returned: always + type: str + sample: fsn1 + city: + description: City of the location + returned: always + type: str + sample: fsn1 + server_types: + description: The Server types the Datacenter can handle + returned: always + type: dict + contains: + available: + description: IDs of Server types that are supported and for which the Datacenter has enough resources left + returned: always + type: list + elements: int + sample: [1, 2, 3] + available_for_migration: + description: IDs of Server types that are supported and for which the Datacenter has enough resources left + returned: always + type: list + elements: int + sample: [1, 2, 3] + supported: + description: IDs of Server types that are supported in the Datacenter + returned: always + type: list + elements: int + sample: [1, 2, 3] +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.datacenters import BoundDatacenter + + +class AnsibleHCloudDatacenterInfo(AnsibleHCloud): + represent = "hcloud_datacenter_info" + + hcloud_datacenter_info: list[BoundDatacenter] | None = None + + def _prepare_result(self): + tmp = [] + + for datacenter in self.hcloud_datacenter_info: + if datacenter is None: + continue + + tmp.append( + { + "id": to_native(datacenter.id), + "name": to_native(datacenter.name), + "description": to_native(datacenter.description), + "location": to_native(datacenter.location.name), + "server_types": { + "available": [o.id for o in datacenter.server_types.available], + "available_for_migration": [o.id for o in datacenter.server_types.available_for_migration], + "supported": [o.id for o in datacenter.server_types.supported], + }, + } + ) + + return tmp + + def get_datacenters(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_datacenter_info = [self.client.datacenters.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None: + self.hcloud_datacenter_info = [self.client.datacenters.get_by_name(self.module.params.get("name"))] + else: + self.hcloud_datacenter_info = self.client.datacenters.get_all() + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudDatacenterInfo.define_module() + hcloud = AnsibleHCloudDatacenterInfo(module) + + hcloud.get_datacenters() + result = hcloud.get_result() + + ansible_info = {"hcloud_datacenter_info": result["hcloud_datacenter_info"]} + module.exit_json(**ansible_info) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_firewall.py b/ansible_collections/hetzner/hcloud/plugins/modules/firewall.py index 34608977e..3c51b5c0a 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_firewall.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/firewall.py @@ -1,20 +1,16 @@ #!/usr/bin/python -# -*- coding: utf-8 -*- # Copyright: (c) 2020, 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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_firewall - +module: firewall short_description: Create and manage firewalls on the Hetzner Cloud. - description: - Create, update and manage firewalls on the Hetzner Cloud. @@ -24,181 +20,233 @@ author: options: id: description: - - The ID of the Hetzner Cloud firewall to manage. - - Only required if no firewall I(name) is given + - The ID of the Hetzner Cloud Firewall to manage. + - Only required if no firewall O(name) is given. type: int name: description: - - The Name of the Hetzner Cloud firewall to manage. - - Only required if no firewall I(id) is given, or a firewall does not exist. + - The Name of the Hetzner Cloud Firewall to manage. + - Only required if no firewall O(id) is given, or the firewall does not exist. type: str labels: description: - - User-defined labels (key-value pairs) + - User-defined labels (key-value pairs). type: dict rules: description: - - List of rules the firewall should contain. + - List of rules the firewall contain. type: list elements: dict suboptions: - direction: + description: description: - - The direction of the firewall rule. + - User defined description of this rule. type: str - choices: [ in, out ] - port: + direction: description: - - The port of the firewall rule. + - The direction of the firewall rule. type: str + choices: [in, out] protocol: description: - The protocol of the firewall rule. type: str - choices: [ icmp, tcp, udp, esp, gre ] + choices: [icmp, tcp, udp, esp, gre] + port: + description: + - The port or port range allowed by this rule. + - A port range can be specified by separating two ports with a dash, e.g 1024-5000. + - Only used if O(rules[].protocol=tcp) or O(rules[].protocol=udp). + type: str source_ips: description: - - List of CIDRs that are allowed within this rule + - List of CIDRs that are allowed within this rule. + - Use 0.0.0.0/0 to allow all IPv4 addresses and ::/0 to allow all IPv6 addresses. + - Only used if O(rules[].direction=in). type: list elements: str - default: [ ] + default: [] destination_ips: description: - - List of CIDRs that are allowed within this rule + - List of CIDRs that are allowed within this rule. + - Use 0.0.0.0/0 to allow all IPv4 addresses and ::/0 to allow all IPv6 addresses. + - Only used if O(rules[].direction=out). type: list elements: str - default: [ ] - description: - description: - - User defined description of this rule. - type: str + default: [] + force: + description: + - Force the deletion of the Firewall when still in use. + type: bool + default: false state: description: - State of the firewall. default: present - choices: [ absent, present ] + choices: [absent, present] type: str + extends_documentation_fragment: -- hetzner.hcloud.hcloud -''' + - hetzner.hcloud.hcloud +""" EXAMPLES = """ - name: Create a basic firewall - hcloud_firewall: + hetzner.hcloud.firewall: name: my-firewall state: present - name: Create a firewall with rules - hcloud_firewall: + hetzner.hcloud.firewall: name: my-firewall rules: - - direction: in - protocol: icmp - source_ips: - - 0.0.0.0/0 - - ::/0 - description: allow icmp in + - description: allow icmp from everywhere + direction: in + protocol: icmp + source_ips: + - 0.0.0.0/0 + - ::/0 state: present - name: Create a firewall with labels - hcloud_firewall: + hetzner.hcloud.firewall: name: my-firewall labels: - key: value - mylabel: 123 + key: value + mylabel: 123 state: present - name: Ensure the firewall is absent (remove if needed) - hcloud_firewall: + hetzner.hcloud.firewall: name: my-firewall state: absent """ RETURN = """ hcloud_firewall: - description: The firewall instance - returned: Always - type: complex + description: The firewall instance. + returned: always + type: dict contains: id: - description: Numeric identifier of the firewall + description: Numeric identifier of the firewall. returned: always type: int sample: 1937415 name: - description: Name of the firewall + description: Name of the firewall. returned: always type: str - sample: my firewall + sample: my-firewall + labels: + description: User-defined labels (key-value pairs). + returned: always + type: dict rules: - description: List of Rules within this Firewall + description: List of rules the firewall contain. returned: always - type: complex + type: list + elements: dict contains: + description: + description: User defined description of this rule. + type: str + returned: always + sample: allow http from anywhere direction: - description: Direction of the Firewall Rule + description: The direction of the firewall rule. type: str returned: always sample: in protocol: - description: Protocol of the Firewall Rule + description: The protocol of the firewall rule. type: str returned: always - sample: icmp + sample: tcp port: - description: Port of the Firewall Rule, None/Null if protocol is icmp + description: The port or port range allowed by this rule. type: str - returned: always - sample: in + returned: if RV(hcloud_firewall.rules[].protocol=tcp) or RV(hcloud_firewall.rules[].protocol=udp) + sample: "80" source_ips: - description: Source IPs of the Firewall + description: List of source CIDRs that are allowed within this rule. type: list elements: str returned: always + sample: ["0.0.0.0/0", "::/0"] destination_ips: - description: Source IPs of the Firewall + description: List of destination CIDRs that are allowed within this rule. type: list elements: str returned: always - description: - description: User defined description of the Firewall Rule - type: str - returned: always - labels: - description: User-defined labels (key-value pairs) + sample: [] + applied_to: + description: List of Resources the Firewall is applied to. returned: always - type: dict + type: list + elements: dict + contains: + type: + description: Type of the resource. + type: str + choices: [server, label_selector] + sample: label_selector + server: + description: ID of the server. + type: int + sample: 12345 + label_selector: + description: Label selector value. + type: str + sample: env=prod + applied_to_resources: + description: List of Resources the Firewall label selector is applied to. + returned: if RV(hcloud_firewall.applied_to[].type=label_selector) + type: list + elements: dict + contains: + type: + description: Type of resource referenced. + type: str + choices: [server] + sample: server + server: + description: ID of the Server. + type: int + sample: 12345 """ -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud import time -try: - from hcloud.firewalls.domain import FirewallRule - from hcloud import APIException -except ImportError: - APIException = None - FirewallRule = None +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import APIException, HCloudException +from ..module_utils.vendor.hcloud.firewalls import ( + BoundFirewall, + FirewallResource, + FirewallRule, +) -class AnsibleHcloudFirewall(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_firewall") - self.hcloud_firewall = None +class AnsibleHCloudFirewall(AnsibleHCloud): + represent = "hcloud_firewall" + + hcloud_firewall: BoundFirewall | None = None def _prepare_result(self): return { "id": to_native(self.hcloud_firewall.id), "name": to_native(self.hcloud_firewall.name), "rules": [self._prepare_result_rule(rule) for rule in self.hcloud_firewall.rules], - "labels": self.hcloud_firewall.labels + "labels": self.hcloud_firewall.labels, + "applied_to": [self._prepare_result_applied_to(resource) for resource in self.hcloud_firewall.applied_to], } - def _prepare_result_rule(self, rule): + def _prepare_result_rule(self, rule: FirewallRule): return { - "direction": rule.direction, + "direction": to_native(rule.direction), "protocol": to_native(rule.protocol), "port": to_native(rule.port) if rule.port is not None else None, "source_ips": [to_native(cidr) for cidr in rule.source_ips], @@ -206,27 +254,39 @@ class AnsibleHcloudFirewall(Hcloud): "description": to_native(rule.description) if rule.description is not None else None, } + def _prepare_result_applied_to(self, resource: FirewallResource): + result = { + "type": to_native(resource.type), + "server": to_native(resource.server.id) if resource.server is not None else None, + "label_selector": ( + to_native(resource.label_selector.selector) if resource.label_selector is not None else None + ), + } + if resource.applied_to_resources is not None: + result["applied_to_resources"] = [ + { + "type": to_native(item.type), + "server": to_native(item.server.id) if item.server is not None else None, + } + for item in resource.applied_to_resources + ] + return result + def _get_firewall(self): try: if self.module.params.get("id") is not None: - self.hcloud_firewall = self.client.firewalls.get_by_id( - self.module.params.get("id") - ) + self.hcloud_firewall = self.client.firewalls.get_by_id(self.module.params.get("id")) elif self.module.params.get("name") is not None: - self.hcloud_firewall = self.client.firewalls.get_by_name( - self.module.params.get("name") - ) + self.hcloud_firewall = self.client.firewalls.get_by_name(self.module.params.get("name")) - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) def _create_firewall(self): - self.module.fail_on_missing_params( - required_params=["name"] - ) + self.module.fail_on_missing_params(required_params=["name"]) params = { "name": self.module.params.get("name"), - "labels": self.module.params.get("labels") + "labels": self.module.params.get("labels"), } rules = self.module.params.get("rules") if rules is not None: @@ -241,20 +301,20 @@ class AnsibleHcloudFirewall(Hcloud): ) for rule in rules ] + if not self.module.check_mode: try: self.client.firewalls.create(**params) - except Exception as e: - self.module.fail_json(msg=e.message, **params) + except HCloudException as exception: + self.fail_json_hcloud(exception, params=params) + self._mark_as_changed() self._get_firewall() def _update_firewall(self): name = self.module.params.get("name") if name is not None and self.hcloud_firewall.name != name: - self.module.fail_on_missing_params( - required_params=["id"] - ) + self.module.fail_on_missing_params(required_params=["id"]) if not self.module.check_mode: self.hcloud_firewall.update(name=name) self._mark_as_changed() @@ -281,6 +341,7 @@ class AnsibleHcloudFirewall(Hcloud): ] self.hcloud_firewall.set_rules(new_rules) self._mark_as_changed() + self._get_firewall() def present_firewall(self): @@ -294,58 +355,76 @@ class AnsibleHcloudFirewall(Hcloud): self._get_firewall() if self.hcloud_firewall is not None: if not self.module.check_mode: + if self.hcloud_firewall.applied_to: + if self.module.params.get("force"): + actions = self.hcloud_firewall.remove_from_resources(self.hcloud_firewall.applied_to) + for action in actions: + action.wait_until_finished() + else: + self.module.warn( + f"Firewall {self.hcloud_firewall.name} is currently used by " + "other resources. You need to unassign the resources before " + "deleting the Firewall or use force=true." + ) + retry_count = 0 - while retry_count < 10: + while True: try: - self.client.firewalls.delete(self.hcloud_firewall) + self.hcloud_firewall.delete() break - except APIException as e: - if "is still in use" in e.message: - retry_count = retry_count + 1 + except APIException as exception: + if "is still in use" in exception.message and retry_count < 10: + retry_count += 1 time.sleep(0.5 * retry_count) - else: - self.module.fail_json(msg=e.message) - except Exception as e: - self.module.fail_json(msg=e.message) + continue + self.fail_json_hcloud(exception) + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._mark_as_changed() self.hcloud_firewall = None - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, name={"type": "str"}, + labels={"type": "dict"}, rules=dict( type="list", elements="dict", options=dict( + description={"type": "str"}, direction={"type": "str", "choices": ["in", "out"]}, protocol={"type": "str", "choices": ["icmp", "udp", "tcp", "esp", "gre"]}, port={"type": "str"}, source_ips={"type": "list", "elements": "str", "default": []}, destination_ips={"type": "list", "elements": "str", "default": []}, - description={"type": "str"}, ), required_together=[["direction", "protocol"]], + required_if=[ + ["protocol", "udp", ["port"]], + ["protocol", "tcp", ["port"]], + ], ), - labels={"type": "dict"}, + force={"type": "bool", "default": False}, state={ "choices": ["absent", "present"], "default": "present", }, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), - required_one_of=[['id', 'name']], - required_if=[['state', 'present', ['name']]], + required_one_of=[["id", "name"]], + required_if=[["state", "present", ["name"]]], supports_check_mode=True, ) def main(): - module = AnsibleHcloudFirewall.define_module() + module = AnsibleHCloudFirewall.define_module() - hcloud = AnsibleHcloudFirewall(module) + hcloud = AnsibleHCloudFirewall(module) state = module.params.get("state") if state == "absent": hcloud.delete_firewall() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/firewall_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/firewall_info.py new file mode 100644 index 000000000..7e7a623d0 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/firewall_info.py @@ -0,0 +1,246 @@ +#!/usr/bin/python + +# 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 annotations + +DOCUMENTATION = """ +--- +module: firewall_info +short_description: Gather infos about the Hetzner Cloud Firewalls. + +description: + - Gather facts about your Hetzner Cloud Firewalls. + +author: + - Jonas Lammler (@jooola) + +options: + id: + description: + - The ID of the Firewall you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name for the Firewall you want to get. + type: str + label_selector: + description: + - The label selector for the Firewalls you want to get. + type: str + +extends_documentation_fragment: + - hetzner.hcloud.hcloud +""" + +EXAMPLES = """ +- name: Gather hcloud Firewall infos + hetzner.hcloud.firewall_info: + register: output + +- name: Print the gathered infos + debug: + var: output +""" + +RETURN = """ +hcloud_firewall_info: + description: List of Firewalls. + returned: always + type: list + elements: dict + contains: + id: + description: Numeric identifier of the firewall. + returned: always + type: int + sample: 1937415 + name: + description: Name of the firewall. + returned: always + type: str + sample: my-firewall + labels: + description: User-defined labels (key-value pairs). + returned: always + type: dict + rules: + description: List of rules the firewall contain. + returned: always + type: list + elements: dict + contains: + description: + description: User defined description of this rule. + type: str + returned: always + sample: allow http from anywhere + direction: + description: The direction of the firewall rule. + type: str + returned: always + sample: in + protocol: + description: The protocol of the firewall rule. + type: str + returned: always + sample: tcp + port: + description: The port or port range allowed by this rule. + type: str + returned: if RV(hcloud_firewall_info[].rules[].protocol=tcp) or RV(hcloud_firewall_info[].rules[].protocol=udp) + sample: "80" + source_ips: + description: List of source CIDRs that are allowed within this rule. + type: list + elements: str + returned: always + sample: ["0.0.0.0/0", "::/0"] + destination_ips: + description: List of destination CIDRs that are allowed within this rule. + type: list + elements: str + returned: always + sample: [] + applied_to: + description: List of Resources the Firewall is applied to. + returned: always + type: list + elements: dict + contains: + type: + description: Type of the resource. + type: str + choices: [server, label_selector] + sample: label_selector + server: + description: ID of the server. + type: int + sample: 12345 + label_selector: + description: Label selector value. + type: str + sample: env=prod + applied_to_resources: + description: List of Resources the Firewall label selector is applied to. + returned: if RV(hcloud_firewall_info[].applied_to[].type=label_selector) + type: list + elements: dict + contains: + type: + description: Type of resource referenced. + type: str + choices: [server] + sample: server + server: + description: ID of the Server. + type: int + sample: 12345 +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.firewalls import ( + BoundFirewall, + FirewallResource, + FirewallRule, +) + + +class AnsibleHCloudFirewallInfo(AnsibleHCloud): + represent = "hcloud_firewall_info" + + hcloud_firewall_info: list[BoundFirewall] | None = None + + def _prepare_result(self): + tmp = [] + + for firewall in self.hcloud_firewall_info: + if firewall is None: + continue + + tmp.append( + { + "id": to_native(firewall.id), + "name": to_native(firewall.name), + "labels": firewall.labels, + "rules": [self._prepare_result_rule(rule) for rule in firewall.rules], + "applied_to": [self._prepare_result_applied_to(resource) for resource in firewall.applied_to], + } + ) + + return tmp + + def _prepare_result_rule(self, rule: FirewallRule): + return { + "description": to_native(rule.description) if rule.description is not None else None, + "direction": to_native(rule.direction), + "protocol": to_native(rule.protocol), + "port": to_native(rule.port) if rule.port is not None else None, + "source_ips": [to_native(cidr) for cidr in rule.source_ips], + "destination_ips": [to_native(cidr) for cidr in rule.destination_ips], + } + + def _prepare_result_applied_to(self, resource: FirewallResource): + result = { + "type": to_native(resource.type), + "server": to_native(resource.server.id) if resource.server is not None else None, + "label_selector": ( + to_native(resource.label_selector.selector) if resource.label_selector is not None else None + ), + } + if resource.applied_to_resources is not None: + result["applied_to_resources"] = [ + { + "type": to_native(item.type), + "server": to_native(item.server.id) if item.server is not None else None, + } + for item in resource.applied_to_resources + ] + return result + + def get_firewalls(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_firewall_info = [self.client.firewalls.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None: + self.hcloud_firewall_info = [self.client.firewalls.get_by_name(self.module.params.get("name"))] + elif self.module.params.get("label_selector") is not None: + self.hcloud_firewall_info = self.client.firewalls.get_all( + label_selector=self.module.params.get("label_selector") + ) + else: + self.hcloud_firewall_info = self.client.firewalls.get_all() + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + label_selector={"type": "str"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudFirewallInfo.define_module() + hcloud = AnsibleHCloudFirewallInfo(module) + + hcloud.get_firewalls() + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/firewall_resource.py b/ansible_collections/hetzner/hcloud/plugins/modules/firewall_resource.py new file mode 100644 index 000000000..207f27092 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/firewall_resource.py @@ -0,0 +1,243 @@ +#!/usr/bin/python + +# 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 annotations + +DOCUMENTATION = """ +--- +module: firewall_resource +short_description: Manage Resources a Hetzner Cloud Firewall is applied to. + +description: + - Add and Remove Resources a Hetzner Cloud Firewall is applied to. + +author: + - Jonas Lammler (@jooola) + +version_added: 2.5.0 +options: + firewall: + description: + - Name or ID of the Hetzner Cloud Firewall. + type: str + required: true + servers: + description: + - List of Server Name or ID. + type: list + elements: str + label_selectors: + description: + - List of Label Selector. + type: list + elements: str + state: + description: + - State of the firewall resources. + default: present + choices: [absent, present] + type: str + +extends_documentation_fragment: + - hetzner.hcloud.hcloud +""" + +EXAMPLES = """ +- name: Apply a firewall to a list of servers + hetzner.hcloud.firewall_resource: + name: my-firewall + servers: + - my-server + - 3456789 + state: present + +- name: Remove a firewall from a list of servers + hetzner.hcloud.firewall_resource: + name: my-firewall + servers: + - my-server + - 3456789 + state: absent + +- name: Apply a firewall to resources using label selectors + hetzner.hcloud.firewall_resource: + name: my-firewall + label_selectors: + - env=prod + state: present + +- name: Remove a firewall from resources using label selectors + hetzner.hcloud.firewall_resource: + name: my-firewall + label_selectors: + - env=prod + state: absent +""" + +RETURN = """ +hcloud_firewall_resource: + description: The Resources a Hetzner Cloud Firewall is applied to. + returned: always + type: dict + contains: + firewall: + description: + - Name of the Hetzner Cloud Firewall. + type: str + sample: my-firewall + servers: + description: + - List of Server Name. + type: list + elements: str + sample: [my-server1, my-server2] + label_selectors: + description: + - List of Label Selector. + type: list + elements: str + sample: [env=prod] +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.firewalls import ( + BoundFirewall, + FirewallResource, + FirewallResourceLabelSelector, +) +from ..module_utils.vendor.hcloud.servers import BoundServer + + +class AnsibleHCloudFirewallResource(AnsibleHCloud): + represent = "hcloud_firewall_resource" + + hcloud_firewall_resource: BoundFirewall | None = None + + def _prepare_result(self): + servers = [] + label_selectors = [] + for resource in self.hcloud_firewall_resource.applied_to: + if resource.type == FirewallResource.TYPE_SERVER: + servers.append(to_native(resource.server.name)) + elif resource.type == FirewallResource.TYPE_LABEL_SELECTOR: + label_selectors.append(to_native(resource.label_selector.selector)) + + return { + "firewall": to_native(self.hcloud_firewall_resource.name), + "servers": servers, + "label_selectors": label_selectors, + } + + def _get_firewall(self): + try: + self.hcloud_firewall_resource = self._client_get_by_name_or_id( + "firewalls", + self.module.params.get("firewall"), + ) + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _diff_firewall_resources(self, operator) -> list[FirewallResource]: + before = self._prepare_result() + + resources: list[FirewallResource] = [] + + servers: list[str] | None = self.module.params.get("servers") + if servers: + for server_param in servers: + try: + server: BoundServer = self._client_get_by_name_or_id("servers", server_param) + except HCloudException as exception: + self.fail_json_hcloud(exception) + + if operator(server.name, before["servers"]): + resources.append( + FirewallResource( + type=FirewallResource.TYPE_SERVER, + server=server, + ) + ) + + label_selectors = self.module.params.get("label_selectors") + if label_selectors: + for label_selector in label_selectors: + if operator(label_selector, before["label_selectors"]): + resources.append( + FirewallResource( + type=FirewallResource.TYPE_LABEL_SELECTOR, + label_selector=FirewallResourceLabelSelector(selector=label_selector), + ) + ) + + return resources + + def present_firewall_resources(self): + self._get_firewall() + resources = self._diff_firewall_resources( + lambda to_add, before: to_add not in before, + ) + if resources: + if not self.module.check_mode: + actions = self.hcloud_firewall_resource.apply_to_resources(resources=resources) + for action in actions: + action.wait_until_finished() + + self.hcloud_firewall_resource.reload() + + self._mark_as_changed() + + def absent_firewall_resources(self): + self._get_firewall() + resources = self._diff_firewall_resources( + lambda to_remove, before: to_remove in before, + ) + if resources: + if not self.module.check_mode: + actions = self.hcloud_firewall_resource.remove_from_resources(resources=resources) + for action in actions: + action.wait_until_finished() + + self.hcloud_firewall_resource.reload() + + self._mark_as_changed() + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec={ + "firewall": {"type": "str", "required": True}, + "servers": {"type": "list", "elements": "str"}, + "label_selectors": {"type": "list", "elements": "str"}, + "state": { + "choices": ["absent", "present"], + "default": "present", + }, + **super().base_module_arguments(), + }, + required_one_of=[["servers", "label_selectors"]], + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudFirewallResource.define_module() + + hcloud = AnsibleHCloudFirewallResource(module) + state = module.params.get("state") + if state == "absent": + hcloud.absent_firewall_resources() + elif state == "present": + hcloud.present_firewall_resources() + + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_floating_ip.py b/ansible_collections/hetzner/hcloud/plugins/modules/floating_ip.py index 1ee61ea13..e037dd7a1 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_floating_ip.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/floating_ip.py @@ -1,16 +1,14 @@ #!/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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_floating_ip +module: floating_ip short_description: Create and manage cloud Floating IPs on the Hetzner Cloud. @@ -71,40 +69,36 @@ options: choices: [ absent, present ] type: str -requirements: - - hcloud-python >= 1.6.0 - extends_documentation_fragment: - hetzner.hcloud.hcloud - -''' +""" EXAMPLES = """ - name: Create a basic IPv4 Floating IP - hcloud_floating_ip: + hetzner.hcloud.floating_ip: name: my-floating-ip home_location: fsn1 type: ipv4 state: present - name: Create a basic IPv6 Floating IP - hcloud_floating_ip: + hetzner.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: + hetzner.hcloud.floating_ip: name: my-floating-ip server: 1234 state: present - name: Assign a Floating IP to another server - hcloud_floating_ip: + hetzner.hcloud.floating_ip: name: my-floating-ip server: 1234 - force: yes + force: true state: present - name: Floating IP should be absent - hcloud_floating_ip: + hetzner.hcloud.floating_ip: name: my-floating-ip state: absent """ @@ -166,14 +160,17 @@ hcloud_floating_ip: """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.floating_ips import BoundFloatingIP -class AnsibleHcloudFloatingIP(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_floating_ip") - self.hcloud_floating_ip = None +class AnsibleHCloudFloatingIP(AnsibleHCloud): + represent = "hcloud_floating_ip" + + hcloud_floating_ip: BoundFloatingIP | None = None def _prepare_result(self): server = None @@ -195,20 +192,14 @@ class AnsibleHcloudFloatingIP(Hcloud): 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") - ) + 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 Exception as e: - self.module.fail_json(msg=e.message) + self.hcloud_floating_ip = self.client.floating_ips.get_by_name(self.module.params.get("name")) + except HCloudException as exception: + self.fail_json_hcloud(exception) def _create_floating_ip(self): - self.module.fail_on_missing_params( - required_params=["type"] - ) + self.module.fail_on_missing_params(required_params=["type"]) try: params = { "description": self.module.params.get("description"), @@ -216,13 +207,9 @@ class AnsibleHcloudFloatingIP(Hcloud): "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") - ) + 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") - ) + 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") @@ -235,8 +222,8 @@ class AnsibleHcloudFloatingIP(Hcloud): delete_protection = self.module.params.get("delete_protection") if delete_protection is not None: self.hcloud_floating_ip.change_protection(delete=delete_protection).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self._get_floating_ip() @@ -258,21 +245,18 @@ class AnsibleHcloudFloatingIP(Hcloud): if server is not None and self.hcloud_floating_ip.server is not None: if self.module.params.get("force") and server != self.hcloud_floating_ip.server.name: if not self.module.check_mode: - self.hcloud_floating_ip.assign( - self.client.servers.get_by_name(server) - ) + self.hcloud_floating_ip.assign(self.client.servers.get_by_name(server)) self._mark_as_changed() elif server != self.hcloud_floating_ip.server.name: self.module.warn( - "Floating IP is already assigned to another server %s. You need to unassign the Floating IP or use force=yes." - % self.hcloud_floating_ip.server.name + "Floating IP is already assigned to another server " + f"{self.hcloud_floating_ip.server.name}. You need to " + "unassign the Floating IP or use force=true." ) self._mark_as_changed() elif server is not None and self.hcloud_floating_ip.server is None: if not self.module.check_mode: - self.hcloud_floating_ip.assign( - self.client.servers.get_by_name(server) - ) + self.hcloud_floating_ip.assign(self.client.servers.get_by_name(server)) self._mark_as_changed() elif server is None and self.hcloud_floating_ip.server is not None: if not self.module.check_mode: @@ -286,8 +270,8 @@ class AnsibleHcloudFloatingIP(Hcloud): self._mark_as_changed() self._get_floating_ip() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) def present_floating_ip(self): self._get_floating_ip() @@ -305,16 +289,17 @@ class AnsibleHcloudFloatingIP(Hcloud): 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 + "Floating IP is currently assigned to server " + f"{self.hcloud_floating_ip.server.name}. You need to " + "unassign the Floating IP or use force=true." ) self._mark_as_changed() self.hcloud_floating_ip = None - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, @@ -330,18 +315,18 @@ class AnsibleHcloudFloatingIP(Hcloud): "choices": ["absent", "present"], "default": "present", }, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), - required_one_of=[['id', 'name']], - mutually_exclusive=[['home_location', 'server']], + required_one_of=[["id", "name"]], + mutually_exclusive=[["home_location", "server"]], supports_check_mode=True, ) def main(): - module = AnsibleHcloudFloatingIP.define_module() + module = AnsibleHCloudFloatingIP.define_module() - hcloud = AnsibleHcloudFloatingIP(module) + hcloud = AnsibleHCloudFloatingIP(module) state = module.params["state"] if state == "absent": hcloud.delete_floating_ip() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_floating_ip_facts.py b/ansible_collections/hetzner/hcloud/plugins/modules/floating_ip_info.py index 2ec359600..663d29622 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_floating_ip_facts.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/floating_ip_info.py @@ -1,23 +1,19 @@ #!/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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_floating_ip_info +module: floating_ip_info short_description: Gather infos about the Hetzner Cloud Floating IPs. description: - Gather facts about your Hetzner Cloud Floating IPs. - - This module was called C(hcloud_floating_ip_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_floating_ip_facts). - Note that the M(hetzner.hcloud.hcloud_floating_ip_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_floating_ip_info)! author: - Lukas Kaemmerling (@LKaemmerling) @@ -26,7 +22,12 @@ options: id: description: - The ID of the Floating IP you want to get. + - The module will fail if the provided ID is invalid. type: int + name: + description: + - The name for the Floating IP you want to get. + type: str label_selector: description: - The label selector for the Floating IP you want to get. @@ -34,11 +35,11 @@ options: extends_documentation_fragment: - hetzner.hcloud.hcloud -''' +""" EXAMPLES = """ - name: Gather hcloud Floating ip infos - hcloud_floating_ip_info: + hetzner.hcloud.floating_ip_info: register: output - name: Print the gathered infos debug: @@ -99,14 +100,17 @@ hcloud_floating_ip_info: """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.floating_ips import BoundFloatingIP -class AnsibleHcloudFloatingIPInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_floating_ip_info") - self.hcloud_floating_ip_info = None +class AnsibleHCloudFloatingIPInfo(AnsibleHCloud): + represent = "hcloud_floating_ip_info" + + hcloud_floating_ip_info: list[BoundFloatingIP] | None = None def _prepare_result(self): tmp = [] @@ -116,69 +120,60 @@ class AnsibleHcloudFloatingIPInfo(Hcloud): server_name = None if floating_ip.server is not None: server_name = floating_ip.server.name - tmp.append({ - "id": to_native(floating_ip.id), - "name": to_native(floating_ip.name), - "description": to_native(floating_ip.description), - "ip": to_native(floating_ip.ip), - "type": to_native(floating_ip.type), - "server": to_native(server_name), - "home_location": to_native(floating_ip.home_location.name), - "labels": floating_ip.labels, - "delete_protection": floating_ip.protection["delete"], - }) + tmp.append( + { + "id": to_native(floating_ip.id), + "name": to_native(floating_ip.name), + "description": to_native(floating_ip.description), + "ip": to_native(floating_ip.ip), + "type": to_native(floating_ip.type), + "server": to_native(server_name), + "home_location": to_native(floating_ip.home_location.name), + "labels": floating_ip.labels, + "delete_protection": floating_ip.protection["delete"], + } + ) return tmp def get_floating_ips(self): try: if self.module.params.get("id") is not None: - self.hcloud_floating_ip_info = [self.client.floating_ips.get_by_id( - self.module.params.get("id") - )] + self.hcloud_floating_ip_info = [self.client.floating_ips.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None: + self.hcloud_floating_ip_info = [self.client.floating_ips.get_by_name(self.module.params.get("name"))] elif self.module.params.get("label_selector") is not None: self.hcloud_floating_ip_info = self.client.floating_ips.get_all( - label_selector=self.module.params.get("label_selector")) + label_selector=self.module.params.get("label_selector") + ) else: self.hcloud_floating_ip_info = self.client.floating_ips.get_all() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, + name={"type": "str"}, label_selector={"type": "str"}, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), supports_check_mode=True, ) def main(): - module = AnsibleHcloudFloatingIPInfo.define_module() - - is_old_facts = module._name == 'hcloud_floating_ip_facts' - if is_old_facts: - module.deprecate("The 'hcloud_floating_ip_facts' module has been renamed to 'hcloud_floating_ip_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudFloatingIPInfo(module) + module = AnsibleHCloudFloatingIPInfo.define_module() + hcloud = AnsibleHCloudFloatingIPInfo(module) hcloud.get_floating_ips() result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_floating_ip_facts': result['hcloud_floating_ip_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_floating_ip_info': result['hcloud_floating_ip_info'] - } - module.exit_json(**ansible_info) + + ansible_info = {"hcloud_floating_ip_info": result["hcloud_floating_ip_info"]} + module.exit_json(**ansible_info) if __name__ == "__main__": diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_datacenter_facts.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_datacenter_facts.py deleted file mode 100644 index 8cebabf8c..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_datacenter_facts.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/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 - -DOCUMENTATION = ''' ---- -module: hcloud_datacenter_info - -short_description: Gather info about the Hetzner Cloud datacenters. - -description: - - Gather info about your Hetzner Cloud datacenters. - - This module was called C(hcloud_datacenter_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_datacenter_facts). - Note that the M(hetzner.hcloud.hcloud_datacenter_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_datacenter_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the datacenter you want to get. - type: int - name: - description: - - The name of the datacenter you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud datacenter info - hcloud_datacenter_info: - register: output -- name: Print the gathered info - debug: - var: output -""" - -RETURN = """ -hcloud_datacenter_info: - description: - - The datacenter info as list - - This module was called C(hcloud_datacenter_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_datacenter_facts). - Note that the M(hetzner.hcloud.hcloud_datacenter_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_datacenter_info)! - returned: always - type: complex - contains: - id: - description: Numeric identifier of the datacenter - returned: always - type: int - sample: 1937415 - name: - description: Name of the datacenter - returned: always - type: str - sample: fsn1-dc8 - description: - description: Detail description of the datacenter - returned: always - type: str - sample: Falkenstein DC 8 - location: - description: Name of the location where the datacenter resides in - returned: always - type: str - sample: fsn1 - city: - description: City of the location - returned: always - type: str - sample: fsn1 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudDatacenterInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_datacenter_info") - self.hcloud_datacenter_info = None - - def _prepare_result(self): - tmp = [] - - for datacenter in self.hcloud_datacenter_info: - if datacenter is not None: - tmp.append({ - "id": to_native(datacenter.id), - "name": to_native(datacenter.name), - "description": to_native(datacenter.description), - "location": to_native(datacenter.location.name) - }) - - return tmp - - def get_datacenters(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_datacenter_info = [self.client.datacenters.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_datacenter_info = [self.client.datacenters.get_by_name( - self.module.params.get("name") - )] - else: - self.hcloud_datacenter_info = self.client.datacenters.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudDatacenterInfo.define_module() - - is_old_facts = module._name == 'hcloud_datacenter_facts' - if is_old_facts: - module.deprecate("The 'hcloud_datacenter_facts' module has been renamed to 'hcloud_datacenter_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - hcloud = AnsibleHcloudDatacenterInfo(module) - - hcloud.get_datacenters() - result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_datacenter_facts': result['hcloud_datacenter_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_datacenter_info': result['hcloud_datacenter_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_datacenter_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_datacenter_info.py deleted file mode 100644 index 8cebabf8c..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_datacenter_info.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/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 - -DOCUMENTATION = ''' ---- -module: hcloud_datacenter_info - -short_description: Gather info about the Hetzner Cloud datacenters. - -description: - - Gather info about your Hetzner Cloud datacenters. - - This module was called C(hcloud_datacenter_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_datacenter_facts). - Note that the M(hetzner.hcloud.hcloud_datacenter_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_datacenter_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the datacenter you want to get. - type: int - name: - description: - - The name of the datacenter you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud datacenter info - hcloud_datacenter_info: - register: output -- name: Print the gathered info - debug: - var: output -""" - -RETURN = """ -hcloud_datacenter_info: - description: - - The datacenter info as list - - This module was called C(hcloud_datacenter_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_datacenter_facts). - Note that the M(hetzner.hcloud.hcloud_datacenter_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_datacenter_info)! - returned: always - type: complex - contains: - id: - description: Numeric identifier of the datacenter - returned: always - type: int - sample: 1937415 - name: - description: Name of the datacenter - returned: always - type: str - sample: fsn1-dc8 - description: - description: Detail description of the datacenter - returned: always - type: str - sample: Falkenstein DC 8 - location: - description: Name of the location where the datacenter resides in - returned: always - type: str - sample: fsn1 - city: - description: City of the location - returned: always - type: str - sample: fsn1 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudDatacenterInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_datacenter_info") - self.hcloud_datacenter_info = None - - def _prepare_result(self): - tmp = [] - - for datacenter in self.hcloud_datacenter_info: - if datacenter is not None: - tmp.append({ - "id": to_native(datacenter.id), - "name": to_native(datacenter.name), - "description": to_native(datacenter.description), - "location": to_native(datacenter.location.name) - }) - - return tmp - - def get_datacenters(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_datacenter_info = [self.client.datacenters.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_datacenter_info = [self.client.datacenters.get_by_name( - self.module.params.get("name") - )] - else: - self.hcloud_datacenter_info = self.client.datacenters.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudDatacenterInfo.define_module() - - is_old_facts = module._name == 'hcloud_datacenter_facts' - if is_old_facts: - module.deprecate("The 'hcloud_datacenter_facts' module has been renamed to 'hcloud_datacenter_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - hcloud = AnsibleHcloudDatacenterInfo(module) - - hcloud.get_datacenters() - result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_datacenter_facts': result['hcloud_datacenter_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_datacenter_info': result['hcloud_datacenter_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_floating_ip_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_floating_ip_info.py deleted file mode 100644 index 2ec359600..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_floating_ip_info.py +++ /dev/null @@ -1,185 +0,0 @@ -#!/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 - -DOCUMENTATION = ''' ---- -module: hcloud_floating_ip_info - -short_description: Gather infos about the Hetzner Cloud Floating IPs. - -description: - - Gather facts about your Hetzner Cloud Floating IPs. - - This module was called C(hcloud_floating_ip_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_floating_ip_facts). - Note that the M(hetzner.hcloud.hcloud_floating_ip_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_floating_ip_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the Floating IP you want to get. - type: int - label_selector: - description: - - The label selector for the Floating IP you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud Floating ip infos - hcloud_floating_ip_info: - register: output -- name: Print the gathered infos - debug: - var: output -""" - -RETURN = """ -hcloud_floating_ip_info: - description: The Floating ip infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the Floating IP - returned: always - type: int - sample: 1937415 - name: - description: Name of the Floating IP - returned: Always - type: str - sample: my-floating-ip - version_added: "0.1.0" - description: - description: Description of the Floating IP - returned: always - type: str - sample: Falkenstein DC 8 - ip: - description: IP address of the Floating IP - returned: always - type: str - sample: 131.232.99.1 - type: - description: Type of the Floating IP - returned: always - type: str - sample: ipv4 - server: - description: Name of the server where the Floating IP is assigned to. - returned: always - type: str - sample: my-server - home_location: - description: Location the Floating IP was created in - returned: always - type: str - sample: fsn1 - delete_protection: - description: True if the Floating IP is protected for deletion - returned: always - type: bool - version_added: "0.1.0" - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudFloatingIPInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_floating_ip_info") - self.hcloud_floating_ip_info = None - - def _prepare_result(self): - tmp = [] - - for floating_ip in self.hcloud_floating_ip_info: - if floating_ip is not None: - server_name = None - if floating_ip.server is not None: - server_name = floating_ip.server.name - tmp.append({ - "id": to_native(floating_ip.id), - "name": to_native(floating_ip.name), - "description": to_native(floating_ip.description), - "ip": to_native(floating_ip.ip), - "type": to_native(floating_ip.type), - "server": to_native(server_name), - "home_location": to_native(floating_ip.home_location.name), - "labels": floating_ip.labels, - "delete_protection": floating_ip.protection["delete"], - }) - - return tmp - - def get_floating_ips(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_floating_ip_info = [self.client.floating_ips.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("label_selector") is not None: - self.hcloud_floating_ip_info = self.client.floating_ips.get_all( - label_selector=self.module.params.get("label_selector")) - else: - self.hcloud_floating_ip_info = self.client.floating_ips.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - label_selector={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudFloatingIPInfo.define_module() - - is_old_facts = module._name == 'hcloud_floating_ip_facts' - if is_old_facts: - module.deprecate("The 'hcloud_floating_ip_facts' module has been renamed to 'hcloud_floating_ip_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudFloatingIPInfo(module) - - hcloud.get_floating_ips() - result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_floating_ip_facts': result['hcloud_floating_ip_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_floating_ip_info': result['hcloud_floating_ip_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_image_facts.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_image_facts.py deleted file mode 100644 index 8acd8846a..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_image_facts.py +++ /dev/null @@ -1,219 +0,0 @@ -#!/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 - -DOCUMENTATION = ''' ---- -module: hcloud_image_info - -short_description: Gather infos about your Hetzner Cloud images. - - -description: - - Gather infos about your Hetzner Cloud images. - - This module was called C(hcloud_location_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_location_facts). - Note that the M(hetzner.hcloud.hcloud_image_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_image_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the image you want to get. - type: int - name: - description: - - The name of the image you want to get. - type: str - label_selector: - description: - - The label selector for the images you want to get. - type: str - type: - description: - - The type for the images you want to get. - default: system - choices: [ system, snapshot, backup ] - type: str - architecture: - description: - - The architecture for the images you want to get. - type: str - choices: [ x86, arm ] -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud image infos - hcloud_image_info: - register: output - -- name: Print the gathered infos - debug: - var: output -""" - -RETURN = """ -hcloud_image_info: - description: The image infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the image - returned: always - type: int - sample: 1937415 - type: - description: Type of the image - returned: always - type: str - sample: system - status: - description: Status of the image - returned: always - type: str - sample: available - name: - description: Name of the image - returned: always - type: str - sample: ubuntu-18.04 - description: - description: Detail description of the image - returned: always - type: str - sample: Ubuntu 18.04 Standard 64 bit - os_flavor: - description: OS flavor of the image - returned: always - type: str - sample: ubuntu - os_version: - description: OS version of the image - returned: always - type: str - sample: 18.04 - architecture: - description: Image is compatible with this architecture - returned: always - type: str - sample: x86 - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudImageInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_image_info") - self.hcloud_image_info = None - - def _prepare_result(self): - tmp = [] - - for image in self.hcloud_image_info: - if image is not None: - tmp.append({ - "id": to_native(image.id), - "status": to_native(image.status), - "type": to_native(image.type), - "name": to_native(image.name), - "description": to_native(image.description), - "os_flavor": to_native(image.os_flavor), - "os_version": to_native(image.os_version), - "architecture": to_native(image.architecture), - "labels": image.labels, - }) - return tmp - - def get_images(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_image_info = [self.client.images.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None and self.module.params.get("architecture") is not None: - self.hcloud_image_info = [self.client.images.get_by_name_and_architecture( - self.module.params.get("name"), - self.module.params.get("architecture") - )] - elif self.module.params.get("name") is not None: - self.hcloud_image_info = [self.client.images.get_by_name( - self.module.params.get("name") - )] - else: - params = {} - label_selector = self.module.params.get("label_selector") - if label_selector: - params["label_selector"] = label_selector - - image_type = self.module.params.get("type") - if image_type: - params["type"] = image_type - - architecture = self.module.params.get("architecture") - if architecture: - params["architecture"] = architecture - - self.hcloud_image_info = self.client.images.get_all(**params) - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - label_selector={"type": "str"}, - type={"choices": ["system", "snapshot", "backup"], "default": "system", "type": "str"}, - architecture={"choices": ["x86", "arm"], "type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudImageInfo.define_module() - - is_old_facts = module._name == 'hcloud_image_facts' - if is_old_facts: - module.deprecate("The 'hcloud_image_facts' module has been renamed to 'hcloud_image_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudImageInfo(module) - hcloud.get_images() - result = hcloud.get_result() - - if is_old_facts: - ansible_info = { - 'hcloud_imagen_facts': result['hcloud_image_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_image_info': result['hcloud_image_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_location_facts.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_location_facts.py deleted file mode 100644 index 623c6ab68..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_location_facts.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/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 - -DOCUMENTATION = ''' ---- -module: hcloud_location_info - -short_description: Gather infos about your Hetzner Cloud locations. - - -description: - - Gather infos about your Hetzner Cloud locations. - - This module was called C(hcloud_location_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_location_facts). - Note that the M(hetzner.hcloud.hcloud_location_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_location_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the location you want to get. - type: int - name: - description: - - The name of the location you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud location infos - hcloud_location_info: - register: output - -- name: Print the gathered infos - debug: - var: output -""" - -RETURN = """ -hcloud_location_info: - description: The location infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the location - returned: always - type: int - sample: 1937415 - name: - description: Name of the location - returned: always - type: str - sample: fsn1 - description: - description: Detail description of the location - returned: always - type: str - sample: Falkenstein DC Park 1 - country: - description: Country code of the location - returned: always - type: str - sample: DE - city: - description: City of the location - returned: always - type: str - sample: Falkenstein -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudLocationInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_location_info") - self.hcloud_location_info = None - - def _prepare_result(self): - tmp = [] - - for location in self.hcloud_location_info: - if location is not None: - tmp.append({ - "id": to_native(location.id), - "name": to_native(location.name), - "description": to_native(location.description), - "city": to_native(location.city), - "country": to_native(location.country) - }) - return tmp - - def get_locations(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_location_info = [self.client.locations.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_location_info = [self.client.locations.get_by_name( - self.module.params.get("name") - )] - else: - self.hcloud_location_info = self.client.locations.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudLocationInfo.define_module() - - is_old_facts = module._name == 'hcloud_location_facts' - if is_old_facts: - module.deprecate("The 'hcloud_location_info' module has been renamed to 'hcloud_location_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudLocationInfo(module) - hcloud.get_locations() - result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_location_facts': result['hcloud_location_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_location_info': result['hcloud_location_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_facts.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_facts.py deleted file mode 100644 index 102ceec0d..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_facts.py +++ /dev/null @@ -1,244 +0,0 @@ -#!/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 - -DOCUMENTATION = ''' ---- -module: hcloud_server_info - -short_description: Gather infos about your Hetzner Cloud servers. - - -description: - - Gather infos about your Hetzner Cloud servers. - - This module was called C(hcloud_server_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_server_facts). - Note that the M(hetzner.hcloud.hcloud_server_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_server_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the server you want to get. - type: int - name: - description: - - The name of the server you want to get. - type: str - label_selector: - description: - - The label selector for the server you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud server infos - hcloud_server_info: - register: output - -- name: Print the gathered infos - debug: - var: output.hcloud_server_info -""" - -RETURN = """ -hcloud_server_info: - description: The server infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the server - returned: always - type: int - sample: 1937415 - name: - description: Name of the server - returned: always - type: str - sample: my-server - status: - description: Status of the server - returned: always - type: str - sample: running - server_type: - description: Name of the server type of the server - returned: always - type: str - sample: cx11 - ipv4_address: - description: Public IPv4 address of the server - returned: always - type: str - sample: 116.203.104.109 - ipv6: - description: IPv6 network of the server - returned: always - type: str - sample: 2a01:4f8:1c1c:c140::/64 - private_networks: - description: List of private networks the server is attached to (name) - returned: always - type: list - elements: str - sample: ['my-network', 'another-network'] - private_networks_info: - description: List of private networks the server is attached to (dict with name and ip) - returned: always - type: list - elements: dict - sample: [{'name': 'my-network', 'ip': '192.168.1.1'}, {'name': 'another-network', 'ip': '10.185.50.40'}] - location: - description: Name of the location of the server - returned: always - type: str - sample: fsn1 - placement_group: - description: Placement Group of the server - type: str - returned: always - sample: 4711 - version_added: "1.5.0" - datacenter: - description: Name of the datacenter of the server - returned: always - type: str - sample: fsn1-dc14 - rescue_enabled: - description: True if rescue mode is enabled, Server will then boot into rescue system on next reboot - returned: always - type: bool - sample: false - backup_window: - description: Time window (UTC) in which the backup will run, or null if the backups are not enabled - returned: always - type: bool - sample: 22-02 - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict - delete_protection: - description: True if server is protected for deletion - type: bool - returned: always - sample: false - version_added: "0.1.0" - rebuild_protection: - description: True if server is protected for rebuild - type: bool - returned: always - sample: false - version_added: "0.1.0" -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudServerInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_server_info") - self.hcloud_server_info = None - - def _prepare_result(self): - tmp = [] - - for server in self.hcloud_server_info: - if server is not None: - image = None if server.image is None else to_native(server.image.name) - placement_group = None if server.placement_group is None else to_native(server.placement_group.name) - ipv4_address = None if server.public_net.ipv4 is None else to_native(server.public_net.ipv4.ip) - ipv6 = None if server.public_net.ipv6 is None else to_native(server.public_net.ipv6.ip) - backup_window = None if server.backup_window is None else to_native(server.backup_window) - tmp.append({ - "id": to_native(server.id), - "name": to_native(server.name), - "ipv4_address": ipv4_address, - "ipv6": ipv6, - "private_networks": [to_native(net.network.name) for net in server.private_net], - "private_networks_info": [{"name": to_native(net.network.name), "ip": net.ip} for net in server.private_net], - "image": image, - "server_type": to_native(server.server_type.name), - "datacenter": to_native(server.datacenter.name), - "location": to_native(server.datacenter.location.name), - "placement_group": placement_group, - "rescue_enabled": server.rescue_enabled, - "backup_window": backup_window, - "labels": server.labels, - "status": to_native(server.status), - "delete_protection": server.protection["delete"], - "rebuild_protection": server.protection["rebuild"], - }) - return tmp - - def get_servers(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_server_info = [self.client.servers.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_server_info = [self.client.servers.get_by_name( - self.module.params.get("name") - )] - elif self.module.params.get("label_selector") is not None: - self.hcloud_server_info = self.client.servers.get_all( - label_selector=self.module.params.get("label_selector")) - else: - self.hcloud_server_info = self.client.servers.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - label_selector={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudServerInfo.define_module() - - is_old_facts = module._name == 'hcloud_server_facts' - if is_old_facts: - module.deprecate("The 'hcloud_server_facts' module has been renamed to 'hcloud_server_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudServerInfo(module) - hcloud.get_servers() - result = hcloud.get_result() - - if is_old_facts: - ansible_info = { - 'hcloud_server_facts': result['hcloud_server_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_server_info': result['hcloud_server_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_type_facts.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_type_facts.py deleted file mode 100644 index a84067c32..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_type_facts.py +++ /dev/null @@ -1,183 +0,0 @@ -#!/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 - -DOCUMENTATION = ''' ---- -module: hcloud_server_type_info - -short_description: Gather infos about the Hetzner Cloud server types. - - -description: - - Gather infos about your Hetzner Cloud server types. - - This module was called C(hcloud_server_type_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_server_type_facts). - Note that the M(hetzner.hcloud.hcloud_server_type_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_server_type_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the server type you want to get. - type: int - name: - description: - - The name of the server type you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud server type infos - hcloud_server_type_info: - register: output - -- name: Print the gathered infos - debug: - var: output.hcloud_server_type_info -""" - -RETURN = """ -hcloud_server_type_info: - description: The server type infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the server type - returned: always - type: int - sample: 1937415 - name: - description: Name of the server type - returned: always - type: str - sample: fsn1 - description: - description: Detail description of the server type - returned: always - type: str - sample: Falkenstein DC Park 1 - cores: - description: Number of cpu cores a server of this type will have - returned: always - type: int - sample: 1 - memory: - description: Memory a server of this type will have in GB - returned: always - type: int - sample: 1 - disk: - description: Disk size a server of this type will have in GB - returned: always - type: int - sample: 25 - storage_type: - description: Type of server boot drive - returned: always - type: str - sample: local - cpu_type: - description: Type of cpu - returned: always - type: str - sample: shared - architecture: - description: Architecture of cpu - returned: always - type: str - sample: x86 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudServerTypeInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_server_type_info") - self.hcloud_server_type_info = None - - def _prepare_result(self): - tmp = [] - - for server_type in self.hcloud_server_type_info: - if server_type is not None: - tmp.append({ - "id": to_native(server_type.id), - "name": to_native(server_type.name), - "description": to_native(server_type.description), - "cores": server_type.cores, - "memory": server_type.memory, - "disk": server_type.disk, - "storage_type": to_native(server_type.storage_type), - "cpu_type": to_native(server_type.cpu_type), - "architecture": to_native(server_type.architecture) - }) - return tmp - - def get_server_types(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_server_type_info = [self.client.server_types.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_server_type_info = [self.client.server_types.get_by_name( - self.module.params.get("name") - )] - else: - self.hcloud_server_type_info = self.client.server_types.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudServerTypeInfo.define_module() - - is_old_facts = module._name == 'hcloud_server_type_facts' - if is_old_facts: - module.deprecate("The 'hcloud_server_type_info' module has been renamed to 'hcloud_server_type_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudServerTypeInfo(module) - hcloud.get_server_types() - result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_server_type_info': result['hcloud_server_type_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_server_type_info': result['hcloud_server_type_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_type_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_type_info.py deleted file mode 100644 index a84067c32..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_type_info.py +++ /dev/null @@ -1,183 +0,0 @@ -#!/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 - -DOCUMENTATION = ''' ---- -module: hcloud_server_type_info - -short_description: Gather infos about the Hetzner Cloud server types. - - -description: - - Gather infos about your Hetzner Cloud server types. - - This module was called C(hcloud_server_type_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_server_type_facts). - Note that the M(hetzner.hcloud.hcloud_server_type_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_server_type_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the server type you want to get. - type: int - name: - description: - - The name of the server type you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud server type infos - hcloud_server_type_info: - register: output - -- name: Print the gathered infos - debug: - var: output.hcloud_server_type_info -""" - -RETURN = """ -hcloud_server_type_info: - description: The server type infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the server type - returned: always - type: int - sample: 1937415 - name: - description: Name of the server type - returned: always - type: str - sample: fsn1 - description: - description: Detail description of the server type - returned: always - type: str - sample: Falkenstein DC Park 1 - cores: - description: Number of cpu cores a server of this type will have - returned: always - type: int - sample: 1 - memory: - description: Memory a server of this type will have in GB - returned: always - type: int - sample: 1 - disk: - description: Disk size a server of this type will have in GB - returned: always - type: int - sample: 25 - storage_type: - description: Type of server boot drive - returned: always - type: str - sample: local - cpu_type: - description: Type of cpu - returned: always - type: str - sample: shared - architecture: - description: Architecture of cpu - returned: always - type: str - sample: x86 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudServerTypeInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_server_type_info") - self.hcloud_server_type_info = None - - def _prepare_result(self): - tmp = [] - - for server_type in self.hcloud_server_type_info: - if server_type is not None: - tmp.append({ - "id": to_native(server_type.id), - "name": to_native(server_type.name), - "description": to_native(server_type.description), - "cores": server_type.cores, - "memory": server_type.memory, - "disk": server_type.disk, - "storage_type": to_native(server_type.storage_type), - "cpu_type": to_native(server_type.cpu_type), - "architecture": to_native(server_type.architecture) - }) - return tmp - - def get_server_types(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_server_type_info = [self.client.server_types.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_server_type_info = [self.client.server_types.get_by_name( - self.module.params.get("name") - )] - else: - self.hcloud_server_type_info = self.client.server_types.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudServerTypeInfo.define_module() - - is_old_facts = module._name == 'hcloud_server_type_facts' - if is_old_facts: - module.deprecate("The 'hcloud_server_type_info' module has been renamed to 'hcloud_server_type_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudServerTypeInfo(module) - hcloud.get_server_types() - result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_server_type_info': result['hcloud_server_type_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_server_type_info': result['hcloud_server_type_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_ssh_key_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_ssh_key_info.py deleted file mode 100644 index aab98ed60..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_ssh_key_info.py +++ /dev/null @@ -1,169 +0,0 @@ -#!/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 - -DOCUMENTATION = ''' ---- -module: hcloud_ssh_key_info -short_description: Gather infos about your Hetzner Cloud ssh_keys. -description: - - Gather facts about your Hetzner Cloud ssh_keys. - - This module was called C(hcloud_ssh_key_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_ssh_key_facts). - Note that the M(hetzner.hcloud.hcloud_ssh_key_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_ssh_key_info)! -author: - - Christopher Schmitt (@cschmitt-hcloud) -options: - id: - description: - - The ID of the ssh key you want to get. - type: int - name: - description: - - The name of the ssh key you want to get. - type: str - fingerprint: - description: - - The fingerprint of the ssh key you want to get. - type: str - label_selector: - description: - - The label selector for the ssh key you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud sshkey infos - hcloud_ssh_key_info: - register: output -- name: Print the gathered infos - debug: - var: output.hcloud_ssh_key_info -""" - -RETURN = """ -hcloud_ssh_key_info: - description: The ssh key instances - returned: Always - type: complex - contains: - id: - description: Numeric identifier of the ssh_key - returned: always - type: int - sample: 1937415 - name: - description: Name of the ssh_key - returned: always - type: str - sample: my-ssh-key - fingerprint: - description: Fingerprint of the ssh key - returned: always - type: str - sample: 0e:e0:bd:c7:2d:1f:69:49:94:44:91:f1:19:fd:35:f3 - public_key: - description: The actual public key - returned: always - type: str - sample: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGpl/tnk74nnQJxxLAtutUApUZMRJxryKh7VXkNbd4g9 john@example.com" - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict -""" -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudSSHKeyInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_ssh_key_info") - self.hcloud_ssh_key_info = None - - def _prepare_result(self): - ssh_keys = [] - - for ssh_key in self.hcloud_ssh_key_info: - if ssh_key: - ssh_keys.append({ - "id": to_native(ssh_key.id), - "name": to_native(ssh_key.name), - "fingerprint": to_native(ssh_key.fingerprint), - "public_key": to_native(ssh_key.public_key), - "labels": ssh_key.labels - }) - return ssh_keys - - def get_ssh_keys(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_ssh_key_info = [self.client.ssh_keys.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_ssh_key_info = [self.client.ssh_keys.get_by_name( - self.module.params.get("name") - )] - elif self.module.params.get("fingerprint") is not None: - self.hcloud_ssh_key_info = [self.client.ssh_keys.get_by_fingerprint( - self.module.params.get("fingerprint") - )] - elif self.module.params.get("label_selector") is not None: - self.hcloud_ssh_key_info = self.client.ssh_keys.get_all( - label_selector=self.module.params.get("label_selector")) - else: - self.hcloud_ssh_key_info = self.client.ssh_keys.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - fingerprint={"type": "str"}, - label_selector={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudSSHKeyInfo.define_module() - - is_old_facts = module._name == 'hcloud_ssh_key_facts' - if is_old_facts: - module.deprecate("The 'hcloud_ssh_key_facts' module has been renamed to 'hcloud_ssh_key_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudSSHKeyInfo(module) - hcloud.get_ssh_keys() - result = hcloud.get_result() - - if is_old_facts: - ansible_info = { - 'hcloud_ssh_key_facts': result['hcloud_ssh_key_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_ssh_key_info': result['hcloud_ssh_key_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_volume_facts.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_volume_facts.py deleted file mode 100644 index 9520bfa14..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_volume_facts.py +++ /dev/null @@ -1,186 +0,0 @@ -#!/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 - -DOCUMENTATION = ''' ---- -module: hcloud_volume_info - -short_description: Gather infos about your Hetzner Cloud Volumes. - -description: - - Gather infos about your Hetzner Cloud Volumes. - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the Volume you want to get. - type: int - name: - description: - - The name of the Volume you want to get. - type: str - label_selector: - description: - - The label selector for the Volume you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud Volume infos - hcloud_volume_info: - register: output -- name: Print the gathered infos - debug: - var: output.hcloud_volume_info -""" - -RETURN = """ -hcloud_volume_info: - description: The Volume infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the Volume - returned: always - type: int - sample: 1937415 - name: - description: Name of the Volume - returned: always - type: str - sample: my-volume - size: - description: Size of the Volume - returned: always - type: str - sample: 10 - linux_device: - description: Path to the device that contains the Volume. - returned: always - type: str - sample: /dev/disk/by-id/scsi-0HC_Volume_12345 - version_added: "0.1.0" - location: - description: Name of the location where the Volume resides in - returned: always - type: str - sample: fsn1 - server: - description: Name of the server where the Volume is attached to - returned: always - type: str - sample: my-server - delete_protection: - description: True if the Volume is protected for deletion - returned: always - type: bool - version_added: "0.1.0" - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudVolumeInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_volume_info") - self.hcloud_volume_info = None - - def _prepare_result(self): - tmp = [] - - for volume in self.hcloud_volume_info: - if volume is not None: - server_name = None - if volume.server is not None: - server_name = to_native(volume.server.name) - tmp.append({ - "id": to_native(volume.id), - "name": to_native(volume.name), - "size": volume.size, - "location": to_native(volume.location.name), - "labels": volume.labels, - "server": server_name, - "linux_device": to_native(volume.linux_device), - "delete_protection": volume.protection["delete"], - }) - - return tmp - - def get_volumes(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_volume_info = [self.client.volumes.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_volume_info = [self.client.volumes.get_by_name( - self.module.params.get("name") - )] - elif self.module.params.get("label_selector") is not None: - self.hcloud_volume_info = self.client.volumes.get_all( - label_selector=self.module.params.get("label_selector")) - else: - self.hcloud_volume_info = self.client.volumes.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - label_selector={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudVolumeInfo.define_module() - - is_old_facts = module._name == 'hcloud_volume_facts' - if is_old_facts: - module.deprecate("The 'hcloud_volume_facts' module has been renamed to 'hcloud_volume_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudVolumeInfo(module) - - hcloud.get_volumes() - result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_volume_facts': result['hcloud_volume_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_volume_info': result['hcloud_volume_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_image_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/image_info.py index 8acd8846a..b0d7fc482 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_image_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/image_info.py @@ -1,24 +1,20 @@ #!/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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_image_info +module: image_info short_description: Gather infos about your Hetzner Cloud images. description: - Gather infos about your Hetzner Cloud images. - - This module was called C(hcloud_location_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_location_facts). - Note that the M(hetzner.hcloud.hcloud_image_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_image_info)! author: - Lukas Kaemmerling (@LKaemmerling) @@ -27,6 +23,7 @@ options: id: description: - The ID of the image you want to get. + - The module will fail if the provided ID is invalid. type: int name: description: @@ -50,11 +47,11 @@ options: extends_documentation_fragment: - hetzner.hcloud.hcloud -''' +""" EXAMPLES = """ - name: Gather hcloud image infos - hcloud_image_info: + hetzner.hcloud.image_info: register: output - name: Print the gathered infos @@ -87,7 +84,7 @@ hcloud_image_info: description: Name of the image returned: always type: str - sample: ubuntu-18.04 + sample: ubuntu-22.04 description: description: Detail description of the image returned: always @@ -115,48 +112,54 @@ hcloud_image_info: """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.images import BoundImage -class AnsibleHcloudImageInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_image_info") - self.hcloud_image_info = None +class AnsibleHCloudImageInfo(AnsibleHCloud): + represent = "hcloud_image_info" + + hcloud_image_info: list[BoundImage] | None = None def _prepare_result(self): tmp = [] for image in self.hcloud_image_info: if image is not None: - tmp.append({ - "id": to_native(image.id), - "status": to_native(image.status), - "type": to_native(image.type), - "name": to_native(image.name), - "description": to_native(image.description), - "os_flavor": to_native(image.os_flavor), - "os_version": to_native(image.os_version), - "architecture": to_native(image.architecture), - "labels": image.labels, - }) + tmp.append( + { + "id": to_native(image.id), + "status": to_native(image.status), + "type": to_native(image.type), + "name": to_native(image.name), + "description": to_native(image.description), + "os_flavor": to_native(image.os_flavor), + "os_version": to_native(image.os_version), + "architecture": to_native(image.architecture), + "labels": image.labels, + } + ) return tmp def get_images(self): try: if self.module.params.get("id") is not None: - self.hcloud_image_info = [self.client.images.get_by_id( - self.module.params.get("id") - )] + self.hcloud_image_info = [self.client.images.get_by_id(self.module.params.get("id"))] elif self.module.params.get("name") is not None and self.module.params.get("architecture") is not None: - self.hcloud_image_info = [self.client.images.get_by_name_and_architecture( - self.module.params.get("name"), - self.module.params.get("architecture") - )] + self.hcloud_image_info = [ + self.client.images.get_by_name_and_architecture( + self.module.params.get("name"), + self.module.params.get("architecture"), + ) + ] elif self.module.params.get("name") is not None: - self.hcloud_image_info = [self.client.images.get_by_name( - self.module.params.get("name") - )] + self.module.warn( + "This module only returns x86 images by default. Please set architecture:x86|arm to hide this message." + ) + self.hcloud_image_info = [self.client.images.get_by_name(self.module.params.get("name"))] else: params = {} label_selector = self.module.params.get("label_selector") @@ -173,11 +176,11 @@ class AnsibleHcloudImageInfo(Hcloud): self.hcloud_image_info = self.client.images.get_all(**params) - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, @@ -185,34 +188,21 @@ class AnsibleHcloudImageInfo(Hcloud): label_selector={"type": "str"}, type={"choices": ["system", "snapshot", "backup"], "default": "system", "type": "str"}, architecture={"choices": ["x86", "arm"], "type": "str"}, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), supports_check_mode=True, ) def main(): - module = AnsibleHcloudImageInfo.define_module() - - is_old_facts = module._name == 'hcloud_image_facts' - if is_old_facts: - module.deprecate("The 'hcloud_image_facts' module has been renamed to 'hcloud_image_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") + module = AnsibleHCloudImageInfo.define_module() + hcloud = AnsibleHCloudImageInfo(module) - hcloud = AnsibleHcloudImageInfo(module) hcloud.get_images() result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_imagen_facts': result['hcloud_image_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_image_info': result['hcloud_image_info'] - } - module.exit_json(**ansible_info) + ansible_info = {"hcloud_image_info": result["hcloud_image_info"]} + module.exit_json(**ansible_info) if __name__ == "__main__": diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/iso_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/iso_info.py new file mode 100644 index 000000000..e623d1714 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/iso_info.py @@ -0,0 +1,206 @@ +#!/usr/bin/python + +# Copyright: (c) 2022, Patrice Le Guyader +# heavily inspired by the work of @LKaemmerling +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: iso_info + +short_description: Gather infos about the Hetzner Cloud ISO list. + +description: + - Gather infos about the Hetzner Cloud ISO list. + +author: + - Patrice Le Guyader (@patlegu) + - Lukas Kaemmerling (@LKaemmerling) + +options: + id: + description: + - The ID of the ISO image you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name of the ISO you want to get. + type: str + architecture: + description: + - Filter ISOs with compatible architecture. + type: str + choices: [x86, arm] + include_architecture_wildcard: + description: + - Include ISOs with wildcard architecture (architecture is null). + - Works only if architecture filter is specified. + type: bool + +extends_documentation_fragment: + - hetzner.hcloud.hcloud +""" + +EXAMPLES = """ +- name: Gather hcloud ISO type infos + hetzner.hcloud.iso_info: + register: output + +- name: Print the gathered infos + debug: + var: output.hcloud_iso_info +""" + +RETURN = """ +hcloud_iso_info: + description: The ISO type infos as list + returned: always + type: complex + contains: + id: + description: ID of the ISO + returned: always + type: int + sample: 22110 + name: + description: Unique identifier of the ISO. Only set for public ISOs + returned: always + type: str + sample: debian-12.0.0-amd64-netinst.iso + description: + description: Description of the ISO + returned: always + type: str + sample: Debian 12.0 (amd64/netinstall) + architecture: + description: > + Type of cpu architecture this ISO is compatible with. + None indicates no restriction on the architecture (wildcard). + returned: when supported + type: str + sample: x86 + type: + description: Type of the ISO, can be one of `public`, `private`. + returned: always + type: str + sample: public + deprecated: + description: > + ISO 8601 timestamp of deprecation, None if ISO is still available. + After the deprecation time it will no longer be possible to attach the + ISO to servers. This field is deprecated. Use `deprecation` instead. + returned: always + type: str + sample: "2024-12-01T00:00:00+00:00" + deprecation: + description: > + Describes if, when & how the resources was deprecated. If this field is + set to None the resource is not deprecated. If it has a value, it is + considered deprecated. + returned: if the resource is deprecated + type: dict + contains: + announced: + description: Date of when the deprecation was announced. + returned: always + type: str + sample: "2021-11-01T00:00:00+00:00" + unavailable_after: + description: > + After the time in this field, the resource will not be available + from the general listing endpoint of the resource type, and it can + not be used in new resources. For example, if this is an image, + you can not create new servers with this image after the mentioned + date. + returned: always + type: str + sample: "2021-12-01T00:00:00+00:00" +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.isos import BoundIso + + +class AnsibleHCloudIsoInfo(AnsibleHCloud): + represent = "hcloud_iso_info" + + hcloud_iso_info: list[BoundIso] | None = None + + def _prepare_result(self): + tmp = [] + + for iso_info in self.hcloud_iso_info: + if iso_info is None: + continue + + tmp.append( + { + "id": to_native(iso_info.id), + "name": to_native(iso_info.name), + "description": to_native(iso_info.description), + "type": iso_info.type, + "architecture": iso_info.architecture, + "deprecated": ( + iso_info.deprecation.unavailable_after if iso_info.deprecation is not None else None + ), + "deprecation": ( + { + "announced": iso_info.deprecation.announced.isoformat(), + "unavailable_after": iso_info.deprecation.unavailable_after.isoformat(), + } + if iso_info.deprecation is not None + else None + ), + } + ) + + return tmp + + def get_iso_infos(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_iso_info = [self.client.isos.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None: + self.hcloud_iso_info = [self.client.isos.get_by_name(self.module.params.get("name"))] + else: + self.hcloud_iso_info = self.client.isos.get_all( + architecture=self.module.params.get("architecture"), + include_wildcard_architecture=self.module.params.get("include_wildcard_architecture"), + ) + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + architecture={"type": "str", "choices": ["x86", "arm"]}, + include_architecture_wildcard={"type": "bool"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudIsoInfo.define_module() + hcloud = AnsibleHCloudIsoInfo(module) + hcloud.get_iso_infos() + result = hcloud.get_result() + ansible_info = {"hcloud_iso_info": result["hcloud_iso_info"]} + module.exit_json(**ansible_info) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer.py b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer.py index 9c6c2bbaf..1a0d8712a 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer.py @@ -1,20 +1,17 @@ #!/usr/bin/python -# -*- coding: utf-8 -*- # Copyright: (c) 2020, 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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_load_balancer +module: load_balancer short_description: Create and manage cloud Load Balancers on the Hetzner Cloud. - description: - Create, update and manage cloud Load Balancers on the Hetzner Cloud. @@ -37,6 +34,12 @@ options: - The Load Balancer Type of the Hetzner Cloud Load Balancer to manage. - Required if Load Balancer does not exist. type: str + algorithm: + description: + - Algorithm of the Load Balancer. + type: str + default: round_robin + choices: [round_robin, least_connections] location: description: - Location of Load Balancer. @@ -68,24 +71,21 @@ options: type: str extends_documentation_fragment: - hetzner.hcloud.hcloud - -requirements: - - hcloud-python >= 1.8.0 -''' +""" EXAMPLES = """ - name: Create a basic Load Balancer - hcloud_load_balancer: + hetzner.hcloud.load_balancer: name: my-Load Balancer load_balancer_type: lb11 + algorithm: round_robin location: fsn1 state: present - name: Ensure the Load Balancer is absent (remove if needed) - hcloud_load_balancer: + hetzner.hcloud.load_balancer: name: my-Load Balancer state: absent - """ RETURN = """ @@ -114,6 +114,12 @@ hcloud_load_balancer: returned: always type: str sample: cx11 + algorithm: + description: Algorithm of the Load Balancer. + returned: always + type: str + choices: [round_robin, least_connections] + sample: round_robin ipv4_address: description: Public IPv4 address of the Load Balancer returned: always @@ -146,18 +152,27 @@ hcloud_load_balancer: """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.load_balancers import ( + BoundLoadBalancer, + LoadBalancerAlgorithm, +) -class AnsibleHcloudLoadBalancer(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_load_balancer") - self.hcloud_load_balancer = None + +class AnsibleHCloudLoadBalancer(AnsibleHCloud): + represent = "hcloud_load_balancer" + + hcloud_load_balancer: BoundLoadBalancer | None = None def _prepare_result(self): - private_ipv4_address = None if len(self.hcloud_load_balancer.private_net) == 0 else to_native( - self.hcloud_load_balancer.private_net[0].ip) + private_ipv4_address = ( + None + if len(self.hcloud_load_balancer.private_net) == 0 + else to_native(self.hcloud_load_balancer.private_net[0].ip) + ) return { "id": to_native(self.hcloud_load_balancer.id), "name": to_native(self.hcloud_load_balancer.name), @@ -165,6 +180,7 @@ class AnsibleHcloudLoadBalancer(Hcloud): "ipv6_address": to_native(self.hcloud_load_balancer.public_net.ipv6.ip), "private_ipv4_address": private_ipv4_address, "load_balancer_type": to_native(self.hcloud_load_balancer.load_balancer_type.name), + "algorithm": to_native(self.hcloud_load_balancer.algorithm.type), "location": to_native(self.hcloud_load_balancer.location.name), "labels": self.hcloud_load_balancer.labels, "delete_protection": self.hcloud_load_balancer.protection["delete"], @@ -174,24 +190,18 @@ class AnsibleHcloudLoadBalancer(Hcloud): def _get_load_balancer(self): try: if self.module.params.get("id") is not None: - self.hcloud_load_balancer = self.client.load_balancers.get_by_id( - self.module.params.get("id") - ) + self.hcloud_load_balancer = self.client.load_balancers.get_by_id(self.module.params.get("id")) else: - self.hcloud_load_balancer = self.client.load_balancers.get_by_name( - self.module.params.get("name") - ) - except Exception as e: - self.module.fail_json(msg=e.message) + self.hcloud_load_balancer = self.client.load_balancers.get_by_name(self.module.params.get("name")) + except HCloudException as exception: + self.fail_json_hcloud(exception) def _create_load_balancer(self): - - self.module.fail_on_missing_params( - required_params=["name", "load_balancer_type"] - ) + self.module.fail_on_missing_params(required_params=["name", "load_balancer_type"]) try: params = { "name": self.module.params.get("name"), + "algorithm": LoadBalancerAlgorithm(type=self.module.params.get("algorithm", "round_robin")), "load_balancer_type": self.client.load_balancer_types.get_by_name( self.module.params.get("load_balancer_type") ), @@ -201,9 +211,7 @@ class AnsibleHcloudLoadBalancer(Hcloud): if self.module.params.get("location") is None and self.module.params.get("network_zone") is None: self.module.fail_json(msg="one of the following is required: location, network_zone") elif self.module.params.get("location") is not None and self.module.params.get("network_zone") is None: - params["location"] = self.client.locations.get_by_name( - self.module.params.get("location") - ) + params["location"] = self.client.locations.get_by_name(self.module.params.get("location")) elif self.module.params.get("location") is None and self.module.params.get("network_zone") is not None: params["network_zone"] = self.module.params.get("network_zone") @@ -215,8 +223,8 @@ class AnsibleHcloudLoadBalancer(Hcloud): if delete_protection is not None: self._get_load_balancer() self.hcloud_load_balancer.change_protection(delete=delete_protection).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self._get_load_balancer() @@ -236,7 +244,9 @@ class AnsibleHcloudLoadBalancer(Hcloud): self._get_load_balancer() disable_public_interface = self.module.params.get("disable_public_interface") - if disable_public_interface is not None and disable_public_interface != (not self.hcloud_load_balancer.public_net.enabled): + if disable_public_interface is not None and disable_public_interface != ( + not self.hcloud_load_balancer.public_net.enabled + ): if not self.module.check_mode: if disable_public_interface is True: self.hcloud_load_balancer.disable_public_interface().wait_until_finished() @@ -245,7 +255,10 @@ class AnsibleHcloudLoadBalancer(Hcloud): self._mark_as_changed() load_balancer_type = self.module.params.get("load_balancer_type") - if load_balancer_type is not None and self.hcloud_load_balancer.load_balancer_type.name != load_balancer_type: + if ( + load_balancer_type is not None + and self.hcloud_load_balancer.load_balancer_type.name != load_balancer_type + ): new_load_balancer_type = self.client.load_balancer_types.get_by_name(load_balancer_type) if not new_load_balancer_type: self.module.fail_json(msg="unknown load balancer type") @@ -255,9 +268,17 @@ class AnsibleHcloudLoadBalancer(Hcloud): ).wait_until_finished(max_retries=1000) self._mark_as_changed() + + algorithm = self.module.params.get("algorithm") + if algorithm is not None and self.hcloud_load_balancer.algorithm.type != algorithm: + self.hcloud_load_balancer.change_algorithm( + algorithm=LoadBalancerAlgorithm(type=algorithm) + ).wait_until_finished() + self._mark_as_changed() + self._get_load_balancer() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) def present_load_balancer(self): self._get_load_balancer() @@ -274,16 +295,17 @@ class AnsibleHcloudLoadBalancer(Hcloud): self.client.load_balancers.delete(self.hcloud_load_balancer) self._mark_as_changed() self.hcloud_load_balancer = None - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, name={"type": "str"}, load_balancer_type={"type": "str"}, + algorithm={"choices": ["round_robin", "least_connections"], "default": "round_robin"}, location={"type": "str"}, network_zone={"type": "str"}, labels={"type": "dict"}, @@ -293,18 +315,18 @@ class AnsibleHcloudLoadBalancer(Hcloud): "choices": ["absent", "present"], "default": "present", }, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), - required_one_of=[['id', 'name']], + required_one_of=[["id", "name"]], mutually_exclusive=[["location", "network_zone"]], supports_check_mode=True, ) def main(): - module = AnsibleHcloudLoadBalancer.define_module() + module = AnsibleHCloudLoadBalancer.define_module() - hcloud = AnsibleHcloudLoadBalancer(module) + hcloud = AnsibleHCloudLoadBalancer(module) state = module.params.get("state") if state == "absent": hcloud.delete_load_balancer() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_info.py index 159dad258..19ead98c2 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_info.py @@ -1,16 +1,14 @@ #!/usr/bin/python -# -*- coding: utf-8 -*- # Copyright: (c) 2020, 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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_load_balancer_info +module: load_balancer_info short_description: Gather infos about your Hetzner Cloud Load Balancers. @@ -25,6 +23,7 @@ options: id: description: - The ID of the Load Balancers you want to get. + - The module will fail if the provided ID is invalid. type: int name: description: @@ -37,11 +36,11 @@ options: extends_documentation_fragment: - hetzner.hcloud.hcloud -''' +""" EXAMPLES = """ - name: Gather hcloud load_balancer infos - hcloud_load_balancer_info: + hetzner.hcloud.load_balancer_info: register: output - name: Print the gathered infos @@ -137,10 +136,27 @@ hcloud_load_balancer_info: use_private_ip: description: - Route the traffic over the private IP of the Load Balancer through a Hetzner Cloud Network. - - Load Balancer needs to be attached to a network. See M(hetzner.hcloud.hcloud.hcloud_load_balancer_network) + - Load Balancer needs to be attached to a network. See M(hetzner.hcloud.load_balancer_network) type: bool sample: true returned: always + health_status: + description: + - List of health statuses of the services on this target. Only present for target types "server" and "ip". + type: list + returned: if I(type) is server or ip + contains: + listen_port: + description: Load Balancer Target listen port + type: int + returned: always + sample: 80 + status: + description: Load Balancer Target status + type: str + choices: [healthy, unhealthy, unknown] + returned: always + sample: healthy services: description: all services from this Load Balancer returned: Always @@ -261,14 +277,17 @@ hcloud_load_balancer_info: """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.load_balancers import BoundLoadBalancer + +class AnsibleHCloudLoadBalancerInfo(AnsibleHCloud): + represent = "hcloud_load_balancer_info" -class AnsibleHcloudLoadBalancerInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_load_balancer_info") - self.hcloud_load_balancer_info = None + hcloud_load_balancer_info: list[BoundLoadBalancer] | None = None def _prepare_result(self): tmp = [] @@ -278,22 +297,25 @@ class AnsibleHcloudLoadBalancerInfo(Hcloud): services = [self._prepare_service_result(service) for service in load_balancer.services] targets = [self._prepare_target_result(target) for target in load_balancer.targets] - private_ipv4_address = None if len(load_balancer.private_net) == 0 else to_native( - load_balancer.private_net[0].ip) - tmp.append({ - "id": to_native(load_balancer.id), - "name": to_native(load_balancer.name), - "ipv4_address": to_native(load_balancer.public_net.ipv4.ip), - "ipv6_address": to_native(load_balancer.public_net.ipv6.ip), - "private_ipv4_address": private_ipv4_address, - "load_balancer_type": to_native(load_balancer.load_balancer_type.name), - "location": to_native(load_balancer.location.name), - "labels": load_balancer.labels, - "delete_protection": load_balancer.protection["delete"], - "disable_public_interface": False if load_balancer.public_net.enabled else True, - "targets": targets, - "services": services - }) + private_ipv4_address = ( + None if len(load_balancer.private_net) == 0 else to_native(load_balancer.private_net[0].ip) + ) + tmp.append( + { + "id": to_native(load_balancer.id), + "name": to_native(load_balancer.name), + "ipv4_address": to_native(load_balancer.public_net.ipv4.ip), + "ipv6_address": to_native(load_balancer.public_net.ipv6.ip), + "private_ipv4_address": private_ipv4_address, + "load_balancer_type": to_native(load_balancer.load_balancer_type.name), + "location": to_native(load_balancer.location.name), + "labels": load_balancer.labels, + "delete_protection": load_balancer.protection["delete"], + "disable_public_interface": False if load_balancer.public_net.enabled else True, + "targets": targets, + "services": services, + } + ) return tmp @staticmethod @@ -305,8 +327,7 @@ class AnsibleHcloudLoadBalancerInfo(Hcloud): "cookie_lifetime": service.http.cookie_name, "redirect_http": service.http.redirect_http, "sticky_sessions": service.http.sticky_sessions, - "certificates": [to_native(certificate.name) for certificate in - service.http.certificates], + "certificates": [to_native(certificate.name) for certificate in service.http.certificates], } health_check = { "protocol": to_native(service.health_check.protocol), @@ -320,8 +341,7 @@ class AnsibleHcloudLoadBalancerInfo(Hcloud): "domain": to_native(service.health_check.http.domain), "path": to_native(service.health_check.http.path), "response": to_native(service.health_check.http.response), - "certificates": [to_native(status_code) for status_code in - service.health_check.http.status_codes], + "certificates": [to_native(status_code) for status_code in service.health_check.http.status_codes], "tls": service.health_check.http.tls, } return { @@ -337,7 +357,7 @@ class AnsibleHcloudLoadBalancerInfo(Hcloud): def _prepare_target_result(target): result = { "type": to_native(target.type), - "use_private_ip": target.use_private_ip + "use_private_ip": target.use_private_ip, } if target.type == "server": result["server"] = to_native(target.server.name) @@ -345,18 +365,26 @@ class AnsibleHcloudLoadBalancerInfo(Hcloud): result["label_selector"] = to_native(target.label_selector.selector) elif target.type == "ip": result["ip"] = to_native(target.ip.ip) + + if target.health_status is not None: + result["health_status"] = [ + { + "listen_port": item.listen_port, + "status": item.status, + } + for item in target.health_status + ] + return result def get_load_balancers(self): try: if self.module.params.get("id") is not None: - self.hcloud_load_balancer_info = [self.client.load_balancers.get_by_id( - self.module.params.get("id") - )] + self.hcloud_load_balancer_info = [self.client.load_balancers.get_by_id(self.module.params.get("id"))] elif self.module.params.get("name") is not None: - self.hcloud_load_balancer_info = [self.client.load_balancers.get_by_name( - self.module.params.get("name") - )] + self.hcloud_load_balancer_info = [ + self.client.load_balancers.get_by_name(self.module.params.get("name")) + ] else: params = {} label_selector = self.module.params.get("label_selector") @@ -365,32 +393,30 @@ class AnsibleHcloudLoadBalancerInfo(Hcloud): self.hcloud_load_balancer_info = self.client.load_balancers.get_all(**params) - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, name={"type": "str"}, label_selector={"type": "str"}, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), supports_check_mode=True, ) def main(): - module = AnsibleHcloudLoadBalancerInfo.define_module() + module = AnsibleHCloudLoadBalancerInfo.define_module() + hcloud = AnsibleHCloudLoadBalancerInfo(module) - hcloud = AnsibleHcloudLoadBalancerInfo(module) hcloud.get_load_balancers() result = hcloud.get_result() - ansible_info = { - 'hcloud_load_balancer_info': result['hcloud_load_balancer_info'] - } + ansible_info = {"hcloud_load_balancer_info": result["hcloud_load_balancer_info"]} module.exit_json(**ansible_info) diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_network.py b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_network.py index 63a7c5471..4560f8735 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_network.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_network.py @@ -1,16 +1,14 @@ #!/usr/bin/python -# -*- coding: utf-8 -*- # Copyright: (c) 2020, 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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_load_balancer_network +module: load_balancer_network short_description: Manage the relationship between Hetzner Cloud Networks and Load Balancers @@ -24,12 +22,12 @@ version_added: 0.1.0 options: network: description: - - The name of the Hetzner Cloud Networks. + - Name or ID of the Hetzner Cloud Networks. type: str required: true load_balancer: description: - - The name of the Hetzner Cloud Load Balancer. + - Name or ID of the Hetzner Cloud Load Balancer. type: str required: true ip: @@ -43,30 +41,26 @@ options: choices: [ absent, present ] type: str -requirements: - - hcloud-python >= 1.8.1 - extends_documentation_fragment: - hetzner.hcloud.hcloud - -''' +""" EXAMPLES = """ - name: Create a basic Load Balancer network - hcloud_load_balancer_network: + hetzner.hcloud.load_balancer_network: network: my-network load_balancer: my-LoadBalancer state: present - name: Create a Load Balancer network and specify the ip address - hcloud_load_balancer_network: + hetzner.hcloud.load_balancer_network: network: my-network load_balancer: my-LoadBalancer ip: 10.0.0.1 state: present - name: Ensure the Load Balancer network is absent (remove if needed) - hcloud_load_balancer_network: + hetzner.hcloud.load_balancer_network: network: my-network load_balancer: my-LoadBalancer state: absent @@ -96,16 +90,20 @@ hcloud_load_balancer_network: """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.load_balancers import BoundLoadBalancer, PrivateNet +from ..module_utils.vendor.hcloud.networks import BoundNetwork -class AnsibleHcloudLoadBalancerNetwork(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_load_balancer_network") - self.hcloud_network = None - self.hcloud_load_balancer = None - self.hcloud_load_balancer_network = None + +class AnsibleHCloudLoadBalancerNetwork(AnsibleHCloud): + represent = "hcloud_load_balancer_network" + + hcloud_network: BoundNetwork | None = None + hcloud_load_balancer: BoundLoadBalancer | None = None + hcloud_load_balancer_network: PrivateNet | None = None def _prepare_result(self): return { @@ -116,31 +114,26 @@ class AnsibleHcloudLoadBalancerNetwork(Hcloud): def _get_load_balancer_and_network(self): try: - network = self.module.params.get("network") - self.hcloud_network = self.client.networks.get_by_name(network) - if not self.hcloud_network: - self.module.fail_json(msg="Network does not exist: %s" % network) - - load_balancer_name = self.module.params.get("load_balancer") - self.hcloud_load_balancer = self.client.load_balancers.get_by_name( - load_balancer_name + self.hcloud_network = self._client_get_by_name_or_id( + "networks", + self.module.params.get("network"), + ) + self.hcloud_load_balancer = self._client_get_by_name_or_id( + "load_balancers", + self.module.params.get("load_balancer"), ) - if not self.hcloud_load_balancer: - self.module.fail_json(msg="Load balancer does not exist: %s" % load_balancer_name) self.hcloud_load_balancer_network = None - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) def _get_load_balancer_network(self): - for privateNet in self.hcloud_load_balancer.private_net: - if privateNet.network.id == self.hcloud_network.id: - self.hcloud_load_balancer_network = privateNet + for private_net in self.hcloud_load_balancer.private_net: + if private_net.network.id == self.hcloud_network.id: + self.hcloud_load_balancer_network = private_net def _create_load_balancer_network(self): - params = { - "network": self.hcloud_network - } + params = {"network": self.hcloud_network} if self.module.params.get("ip") is not None: params["ip"] = self.module.params.get("ip") @@ -148,8 +141,8 @@ class AnsibleHcloudLoadBalancerNetwork(Hcloud): if not self.module.check_mode: try: self.hcloud_load_balancer.attach_to_network(**params).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self._get_load_balancer_and_network() @@ -168,15 +161,16 @@ class AnsibleHcloudLoadBalancerNetwork(Hcloud): if not self.module.check_mode: try: self.hcloud_load_balancer.detach_from_network( - self.hcloud_load_balancer_network.network).wait_until_finished() + self.hcloud_load_balancer_network.network + ).wait_until_finished() self._mark_as_changed() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self.hcloud_load_balancer_network = None - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( network={"type": "str", "required": True}, @@ -186,16 +180,16 @@ class AnsibleHcloudLoadBalancerNetwork(Hcloud): "choices": ["absent", "present"], "default": "present", }, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), supports_check_mode=True, ) def main(): - module = AnsibleHcloudLoadBalancerNetwork.define_module() + module = AnsibleHCloudLoadBalancerNetwork.define_module() - hcloud = AnsibleHcloudLoadBalancerNetwork(module) + hcloud = AnsibleHCloudLoadBalancerNetwork(module) state = module.params["state"] if state == "absent": hcloud.delete_load_balancer_network() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_service.py b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_service.py index b5edcc6b5..1fc18deef 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_service.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_service.py @@ -1,16 +1,14 @@ #!/usr/bin/python -# -*- coding: utf-8 -*- # Copyright: (c) 2020, 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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_load_balancer_service +module: load_balancer_service short_description: Create and manage the services of cloud Load Balancers on the Hetzner Cloud. @@ -24,7 +22,7 @@ version_added: 0.1.0 options: load_balancer: description: - - The Name of the Hetzner Cloud Load Balancer the service belongs to + - Name or ID of the Hetzner Cloud Load Balancer the service belongs to type: str required: true listen_port: @@ -137,21 +135,18 @@ options: type: str extends_documentation_fragment: - hetzner.hcloud.hcloud - -requirements: - - hcloud-python >= 1.8.1 -''' +""" EXAMPLES = """ - name: Create a basic Load Balancer service with Port 80 - hcloud_load_balancer_service: + hetzner.hcloud.load_balancer_service: load_balancer: my-load-balancer protocol: http listen_port: 80 state: present - name: Ensure the Load Balancer is absent (remove if needed) - hcloud_load_balancer_service: + hetzner.hcloud.load_balancer_service: load_balancer: my-Load Balancer protocol: http listen_port: 80 @@ -284,22 +279,24 @@ hcloud_load_balancer_service: """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import APIException, HCloudException +from ..module_utils.vendor.hcloud.load_balancers import ( + BoundLoadBalancer, + LoadBalancerHealtCheckHttp, + LoadBalancerHealthCheck, + LoadBalancerService, + LoadBalancerServiceHttp, +) -try: - from hcloud.load_balancers.domain import LoadBalancerService, LoadBalancerServiceHttp, \ - LoadBalancerHealthCheck, LoadBalancerHealtCheckHttp - from hcloud import APIException -except ImportError: - APIException = None +class AnsibleHCloudLoadBalancerService(AnsibleHCloud): + represent = "hcloud_load_balancer_service" -class AnsibleHcloudLoadBalancerService(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_load_balancer_service") - self.hcloud_load_balancer = None - self.hcloud_load_balancer_service = None + hcloud_load_balancer: BoundLoadBalancer | None = None + hcloud_load_balancer_service: LoadBalancerService | None = None def _prepare_result(self): http = None @@ -309,8 +306,9 @@ class AnsibleHcloudLoadBalancerService(Hcloud): "cookie_lifetime": self.hcloud_load_balancer_service.http.cookie_name, "redirect_http": self.hcloud_load_balancer_service.http.redirect_http, "sticky_sessions": self.hcloud_load_balancer_service.http.sticky_sessions, - "certificates": [to_native(certificate.name) for certificate in - self.hcloud_load_balancer_service.http.certificates], + "certificates": [ + to_native(certificate.name) for certificate in self.hcloud_load_balancer_service.http.certificates + ], } health_check = { "protocol": to_native(self.hcloud_load_balancer_service.health_check.protocol), @@ -324,8 +322,10 @@ class AnsibleHcloudLoadBalancerService(Hcloud): "domain": to_native(self.hcloud_load_balancer_service.health_check.http.domain), "path": to_native(self.hcloud_load_balancer_service.health_check.http.path), "response": to_native(self.hcloud_load_balancer_service.health_check.http.response), - "certificates": [to_native(status_code) for status_code in - self.hcloud_load_balancer_service.health_check.http.status_codes], + "status_codes": [ + to_native(status_code) + for status_code in self.hcloud_load_balancer_service.health_check.http.status_codes + ], "tls": self.hcloud_load_balancer_service.health_check.http.tls, } return { @@ -340,31 +340,23 @@ class AnsibleHcloudLoadBalancerService(Hcloud): def _get_load_balancer(self): try: - load_balancer_name = self.module.params.get("load_balancer") - self.hcloud_load_balancer = self.client.load_balancers.get_by_name( - load_balancer_name + self.hcloud_load_balancer = self._client_get_by_name_or_id( + "load_balancers", + self.module.params.get("load_balancer"), ) - if not self.hcloud_load_balancer: - self.module.fail_json(msg="Load balancer does not exist: %s" % load_balancer_name) - self._get_load_balancer_service() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) def _create_load_balancer_service(self): - - self.module.fail_on_missing_params( - required_params=["protocol"] - ) + self.module.fail_on_missing_params(required_params=["protocol"]) if self.module.params.get("protocol") == "tcp": - self.module.fail_on_missing_params( - required_params=["destination_port"] - ) + self.module.fail_on_missing_params(required_params=["destination_port"]) params = { "protocol": self.module.params.get("protocol"), "listen_port": self.module.params.get("listen_port"), - "proxyprotocol": self.module.params.get("proxyprotocol") + "proxyprotocol": self.module.params.get("proxyprotocol"), } if self.module.params.get("destination_port"): @@ -375,14 +367,16 @@ class AnsibleHcloudLoadBalancerService(Hcloud): if self.module.params.get("health_check"): params["health_check"] = self.__get_service_health_checks( - health_check=self.module.params.get("health_check")) + health_check=self.module.params.get("health_check") + ) if not self.module.check_mode: try: self.hcloud_load_balancer.add_service(LoadBalancerService(**params)).wait_until_finished( - max_retries=1000) - except Exception as e: - self.module.fail_json(msg=e.message) + max_retries=1000 + ) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self._get_load_balancer() self._get_load_balancer_service() @@ -404,15 +398,11 @@ class AnsibleHcloudLoadBalancerService(Hcloud): hcloud_cert = None try: try: - hcloud_cert = self.client.certificates.get_by_name( - certificate - ) + hcloud_cert = self.client.certificates.get_by_name(certificate) except Exception: - hcloud_cert = self.client.certificates.get_by_id( - certificate - ) - except Exception as e: - self.module.fail_json(msg=e.message) + hcloud_cert = self.client.certificates.get_by_id(certificate) + except HCloudException as exception: + self.fail_json_hcloud(exception) service_http.certificates.append(hcloud_cert) return service_http @@ -473,14 +463,16 @@ class AnsibleHcloudLoadBalancerService(Hcloud): if self.module.params.get("health_check") is not None: params["health_check"] = self.__get_service_health_checks( - health_check=self.module.params.get("health_check")) + health_check=self.module.params.get("health_check") + ) changed = True if not self.module.check_mode: self.hcloud_load_balancer.update_service(LoadBalancerService(**params)).wait_until_finished( - max_retries=1000) - except Exception as e: - self.module.fail_json(msg=e.message) + max_retries=1000 + ) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._get_load_balancer() if changed: @@ -505,16 +497,17 @@ class AnsibleHcloudLoadBalancerService(Hcloud): if not self.module.check_mode: try: self.hcloud_load_balancer.delete_service(self.hcloud_load_balancer_service).wait_until_finished( - max_retries=1000) - except Exception as e: - self.module.fail_json(msg=e.message) + max_retries=1000 + ) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self.hcloud_load_balancer_service = None - except APIException as e: - self.module.fail_json(msg=e.message) + except APIException as exception: + self.fail_json_hcloud(exception) - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( load_balancer={"type": "str", "required": True}, @@ -528,26 +521,12 @@ class AnsibleHcloudLoadBalancerService(Hcloud): http={ "type": "dict", "options": dict( - cookie_name={ - "type": "str" - }, - cookie_lifetime={ - "type": "int" - }, - sticky_sessions={ - "type": "bool", - "default": False - }, - redirect_http={ - "type": "bool", - "default": False - }, - certificates={ - "type": "list", - "elements": "str" - }, - - ) + cookie_name={"type": "str"}, + cookie_lifetime={"type": "int"}, + sticky_sessions={"type": "bool", "default": False}, + redirect_http={"type": "bool", "default": False}, + certificates={"type": "list", "elements": "str"}, + ), }, health_check={ "type": "dict", @@ -556,57 +535,36 @@ class AnsibleHcloudLoadBalancerService(Hcloud): "type": "str", "choices": ["http", "https", "tcp"], }, - port={ - "type": "int" - }, - interval={ - "type": "int" - }, - timeout={ - "type": "int" - }, - retries={ - "type": "int" - }, + port={"type": "int"}, + interval={"type": "int"}, + timeout={"type": "int"}, + retries={"type": "int"}, http={ "type": "dict", "options": dict( - domain={ - "type": "str" - }, - path={ - "type": "str" - }, - response={ - "type": "str" - }, - status_codes={ - "type": "list", - "elements": "str" - }, - tls={ - "type": "bool", - "default": False - }, - ) - } - ) - + domain={"type": "str"}, + path={"type": "str"}, + response={"type": "str"}, + status_codes={"type": "list", "elements": "str"}, + tls={"type": "bool", "default": False}, + ), + }, + ), }, state={ "choices": ["absent", "present"], "default": "present", }, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), supports_check_mode=True, ) def main(): - module = AnsibleHcloudLoadBalancerService.define_module() + module = AnsibleHCloudLoadBalancerService.define_module() - hcloud = AnsibleHcloudLoadBalancerService(module) + hcloud = AnsibleHCloudLoadBalancerService(module) state = module.params.get("state") if state == "absent": hcloud.delete_load_balancer_service() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_target.py b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_target.py index 760884466..36e7f608f 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_target.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_target.py @@ -1,16 +1,14 @@ #!/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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_load_balancer_target +module: load_balancer_target short_description: Manage Hetzner Cloud Load Balancer targets @@ -30,12 +28,12 @@ options: required: true load_balancer: description: - - The name of the Hetzner Cloud Load Balancer. + - Name or ID of the Hetzner Cloud Load Balancer. type: str required: true server: description: - - The name of the Hetzner Cloud Server. + - Name or ID of the Hetzner Cloud Server. - Required if I(type) is server type: str label_selector: @@ -51,7 +49,7 @@ options: use_private_ip: description: - Route the traffic over the private IP of the Load Balancer through a Hetzner Cloud Network. - - Load Balancer needs to be attached to a network. See M(hetzner.hcloud.hcloud.hcloud_load_balancer_network) + - Load Balancer needs to be attached to a network. See M(hetzner.hcloud.load_balancer_network) type: bool default: False state: @@ -61,38 +59,34 @@ options: choices: [ absent, present ] type: str -requirements: - - hcloud-python >= 1.8.1 - extends_documentation_fragment: - hetzner.hcloud.hcloud - -''' +""" EXAMPLES = """ - name: Create a server Load Balancer target - hetzner.hcloud.hcloud_load_balancer_target: + hetzner.hcloud.load_balancer_target: type: server load_balancer: my-LoadBalancer server: my-server state: present - name: Create a label_selector Load Balancer target - hetzner.hcloud.hcloud_load_balancer_target: + hetzner.hcloud.load_balancer_target: type: label_selector load_balancer: my-LoadBalancer label_selector: application=backend state: present - name: Create an IP Load Balancer target - hetzner.hcloud.hcloud_load_balancer_target: + hetzner.hcloud.load_balancer_target: type: ip load_balancer: my-LoadBalancer ip: 127.0.0.1 state: present - name: Ensure the Load Balancer target is absent (remove if needed) - hetzner.hcloud.hcloud_load_balancer_target: + hetzner.hcloud.load_balancer_target: type: server load_balancer: my-LoadBalancer server: my-server @@ -133,36 +127,38 @@ hcloud_load_balancer_target: use_private_ip: description: - Route the traffic over the private IP of the Load Balancer through a Hetzner Cloud Network. - - Load Balancer needs to be attached to a network. See M(hetzner.hcloud.hcloud.hcloud_load_balancer_network) + - Load Balancer needs to be attached to a network. See M(hetzner.hcloud.load_balancer_network) type: bool sample: true returned: always """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native -try: - from hcloud.load_balancers.domain import LoadBalancerTarget, LoadBalancerTargetLabelSelector, LoadBalancerTargetIP -except ImportError: - LoadBalancerTarget = None - LoadBalancerTargetLabelSelector = None - LoadBalancerTargetIP = None +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import APIException, HCloudException +from ..module_utils.vendor.hcloud.load_balancers import ( + BoundLoadBalancer, + LoadBalancerTarget, + LoadBalancerTargetIP, + LoadBalancerTargetLabelSelector, +) +from ..module_utils.vendor.hcloud.servers import BoundServer -class AnsibleHcloudLoadBalancerTarget(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_load_balancer_target") - self.hcloud_load_balancer = None - self.hcloud_load_balancer_target = None - self.hcloud_server = None +class AnsibleHCloudLoadBalancerTarget(AnsibleHCloud): + represent = "hcloud_load_balancer_target" + + hcloud_load_balancer: BoundLoadBalancer | None = None + hcloud_load_balancer_target: LoadBalancerTarget | None = None + hcloud_server: BoundServer | None = None def _prepare_result(self): result = { "type": to_native(self.hcloud_load_balancer_target.type), "load_balancer": to_native(self.hcloud_load_balancer.name), - "use_private_ip": self.hcloud_load_balancer_target.use_private_ip + "use_private_ip": self.hcloud_load_balancer_target.use_private_ip, } if self.hcloud_load_balancer_target.type == "server": @@ -175,22 +171,20 @@ class AnsibleHcloudLoadBalancerTarget(Hcloud): def _get_load_balancer_and_target(self): try: - load_balancer_name = self.module.params.get("load_balancer") - self.hcloud_load_balancer = self.client.load_balancers.get_by_name( - load_balancer_name + self.hcloud_load_balancer = self._client_get_by_name_or_id( + "load_balancers", + self.module.params.get("load_balancer"), ) - if not self.hcloud_load_balancer: - self.module.fail_json(msg="Load balancer does not exist: %s" % load_balancer_name) if self.module.params.get("type") == "server": - server_name = self.module.params.get("server") - self.hcloud_server = self.client.servers.get_by_name(server_name) - if not self.hcloud_server: - self.module.fail_json(msg="Server not found: %s" % server_name) + self.hcloud_server = self._client_get_by_name_or_id( + "servers", + self.module.params.get("server"), + ) self.hcloud_load_balancer_target = None - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) def _get_load_balancer_target(self): for target in self.hcloud_load_balancer.targets: @@ -205,40 +199,40 @@ class AnsibleHcloudLoadBalancerTarget(Hcloud): self.hcloud_load_balancer_target = target def _create_load_balancer_target(self): - params = { - "target": None - } + params = {"target": None} if self.module.params.get("type") == "server": - self.module.fail_on_missing_params( - required_params=["server"] + self.module.fail_on_missing_params(required_params=["server"]) + params["target"] = LoadBalancerTarget( + type=self.module.params.get("type"), + server=self.hcloud_server, + use_private_ip=self.module.params.get("use_private_ip"), ) - params["target"] = LoadBalancerTarget(type=self.module.params.get("type"), server=self.hcloud_server, - use_private_ip=self.module.params.get("use_private_ip")) elif self.module.params.get("type") == "label_selector": - self.module.fail_on_missing_params( - required_params=["label_selector"] + self.module.fail_on_missing_params(required_params=["label_selector"]) + params["target"] = LoadBalancerTarget( + type=self.module.params.get("type"), + label_selector=LoadBalancerTargetLabelSelector(selector=self.module.params.get("label_selector")), + use_private_ip=self.module.params.get("use_private_ip"), ) - params["target"] = LoadBalancerTarget(type=self.module.params.get("type"), - label_selector=LoadBalancerTargetLabelSelector( - selector=self.module.params.get("label_selector")), - use_private_ip=self.module.params.get("use_private_ip")) elif self.module.params.get("type") == "ip": - self.module.fail_on_missing_params( - required_params=["ip"] + self.module.fail_on_missing_params(required_params=["ip"]) + params["target"] = LoadBalancerTarget( + type=self.module.params.get("type"), + ip=LoadBalancerTargetIP(ip=self.module.params.get("ip")), + use_private_ip=False, ) - params["target"] = LoadBalancerTarget(type=self.module.params.get("type"), - ip=LoadBalancerTargetIP(ip=self.module.params.get("ip")), - use_private_ip=False) if not self.module.check_mode: try: self.hcloud_load_balancer.add_target(**params).wait_until_finished() - except Exception as e: - if e.code == "locked" or e.code == "conflict": + except APIException as exception: + if exception.code == "locked" or exception.code == "conflict": self._create_load_balancer_target() else: - self.module.fail_json(msg=e.message) + self.fail_json_hcloud(exception) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self._get_load_balancer_and_target() @@ -257,35 +251,33 @@ class AnsibleHcloudLoadBalancerTarget(Hcloud): if not self.module.check_mode: target = None if self.module.params.get("type") == "server": - self.module.fail_on_missing_params( - required_params=["server"] - ) - target = LoadBalancerTarget(type=self.module.params.get("type"), - server=self.hcloud_server) + self.module.fail_on_missing_params(required_params=["server"]) + target = LoadBalancerTarget(type=self.module.params.get("type"), server=self.hcloud_server) elif self.module.params.get("type") == "label_selector": - self.module.fail_on_missing_params( - required_params=["label_selector"] + self.module.fail_on_missing_params(required_params=["label_selector"]) + target = LoadBalancerTarget( + type=self.module.params.get("type"), + label_selector=LoadBalancerTargetLabelSelector( + selector=self.module.params.get("label_selector") + ), + use_private_ip=self.module.params.get("use_private_ip"), ) - target = LoadBalancerTarget(type=self.module.params.get("type"), - label_selector=LoadBalancerTargetLabelSelector( - selector=self.module.params.get("label_selector")), - use_private_ip=self.module.params.get("use_private_ip")) elif self.module.params.get("type") == "ip": - self.module.fail_on_missing_params( - required_params=["ip"] + self.module.fail_on_missing_params(required_params=["ip"]) + target = LoadBalancerTarget( + type=self.module.params.get("type"), + ip=LoadBalancerTargetIP(ip=self.module.params.get("ip")), + use_private_ip=False, ) - target = LoadBalancerTarget(type=self.module.params.get("type"), - ip=LoadBalancerTargetIP(ip=self.module.params.get("ip")), - use_private_ip=False) try: self.hcloud_load_balancer.remove_target(target).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self.hcloud_load_balancer_target = None - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( type={"type": "str", "required": True, "choices": ["server", "label_selector", "ip"]}, @@ -298,16 +290,16 @@ class AnsibleHcloudLoadBalancerTarget(Hcloud): "choices": ["absent", "present"], "default": "present", }, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), supports_check_mode=True, ) def main(): - module = AnsibleHcloudLoadBalancerTarget.define_module() + module = AnsibleHCloudLoadBalancerTarget.define_module() - hcloud = AnsibleHcloudLoadBalancerTarget(module) + hcloud = AnsibleHCloudLoadBalancerTarget(module) state = module.params["state"] if state == "absent": hcloud.delete_load_balancer_target() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_type_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_type_info.py index a481ea9c9..67feafd59 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_type_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_type_info.py @@ -1,16 +1,14 @@ #!/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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_load_balancer_type_info +module: load_balancer_type_info short_description: Gather infos about the Hetzner Cloud Load Balancer types. @@ -25,6 +23,7 @@ options: id: description: - The ID of the Load Balancer type you want to get. + - The module will fail if the provided ID is invalid. type: int name: description: @@ -33,11 +32,11 @@ options: extends_documentation_fragment: - hetzner.hcloud.hcloud -''' +""" EXAMPLES = """ - name: Gather hcloud Load Balancer type infos - hcloud_load_balancer_type_info: + hetzner.hcloud.load_balancer_type_info: register: output - name: Print the gathered infos @@ -89,68 +88,72 @@ hcloud_load_balancer_type_info: """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.load_balancer_types import BoundLoadBalancerType -class AnsibleHcloudLoadBalancerTypeInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_load_balancer_type_info") - self.hcloud_load_balancer_type_info = None +class AnsibleHCloudLoadBalancerTypeInfo(AnsibleHCloud): + represent = "hcloud_load_balancer_type_info" + + hcloud_load_balancer_type_info: list[BoundLoadBalancerType] | None = None def _prepare_result(self): tmp = [] for load_balancer_type in self.hcloud_load_balancer_type_info: if load_balancer_type is not None: - tmp.append({ - "id": to_native(load_balancer_type.id), - "name": to_native(load_balancer_type.name), - "description": to_native(load_balancer_type.description), - "max_connections": load_balancer_type.max_connections, - "max_services": load_balancer_type.max_services, - "max_targets": load_balancer_type.max_targets, - "max_assigned_certificates": load_balancer_type.max_assigned_certificates - }) + tmp.append( + { + "id": to_native(load_balancer_type.id), + "name": to_native(load_balancer_type.name), + "description": to_native(load_balancer_type.description), + "max_connections": load_balancer_type.max_connections, + "max_services": load_balancer_type.max_services, + "max_targets": load_balancer_type.max_targets, + "max_assigned_certificates": load_balancer_type.max_assigned_certificates, + } + ) return tmp def get_load_balancer_types(self): try: if self.module.params.get("id") is not None: - self.hcloud_load_balancer_type_info = [self.client.load_balancer_types.get_by_id( - self.module.params.get("id") - )] + self.hcloud_load_balancer_type_info = [ + self.client.load_balancer_types.get_by_id(self.module.params.get("id")) + ] elif self.module.params.get("name") is not None: - self.hcloud_load_balancer_type_info = [self.client.load_balancer_types.get_by_name( - self.module.params.get("name") - )] + self.hcloud_load_balancer_type_info = [ + self.client.load_balancer_types.get_by_name(self.module.params.get("name")) + ] else: self.hcloud_load_balancer_type_info = self.client.load_balancer_types.get_all() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, name={"type": "str"}, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), supports_check_mode=True, ) def main(): - module = AnsibleHcloudLoadBalancerTypeInfo.define_module() + module = AnsibleHCloudLoadBalancerTypeInfo.define_module() + hcloud = AnsibleHCloudLoadBalancerTypeInfo(module) - hcloud = AnsibleHcloudLoadBalancerTypeInfo(module) hcloud.get_load_balancer_types() result = hcloud.get_result() - ansible_info = { - 'hcloud_load_balancer_type_info': result['hcloud_load_balancer_type_info'] - } + + ansible_info = {"hcloud_load_balancer_type_info": result["hcloud_load_balancer_type_info"]} module.exit_json(**ansible_info) diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_location_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/location_info.py index 623c6ab68..ac495c6c8 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_location_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/location_info.py @@ -1,24 +1,20 @@ #!/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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_location_info +module: location_info short_description: Gather infos about your Hetzner Cloud locations. description: - Gather infos about your Hetzner Cloud locations. - - This module was called C(hcloud_location_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_location_facts). - Note that the M(hetzner.hcloud.hcloud_location_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_location_info)! author: - Lukas Kaemmerling (@LKaemmerling) @@ -27,6 +23,7 @@ options: id: description: - The ID of the location you want to get. + - The module will fail if the provided ID is invalid. type: int name: description: @@ -35,11 +32,11 @@ options: extends_documentation_fragment: - hetzner.hcloud.hcloud -''' +""" EXAMPLES = """ - name: Gather hcloud location infos - hcloud_location_info: + hetzner.hcloud.location_info: register: output - name: Print the gathered infos @@ -81,78 +78,67 @@ hcloud_location_info: """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.locations import BoundLocation + +class AnsibleHCloudLocationInfo(AnsibleHCloud): + represent = "hcloud_location_info" -class AnsibleHcloudLocationInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_location_info") - self.hcloud_location_info = None + hcloud_location_info: list[BoundLocation] | None = None def _prepare_result(self): tmp = [] for location in self.hcloud_location_info: if location is not None: - tmp.append({ - "id": to_native(location.id), - "name": to_native(location.name), - "description": to_native(location.description), - "city": to_native(location.city), - "country": to_native(location.country) - }) + tmp.append( + { + "id": to_native(location.id), + "name": to_native(location.name), + "description": to_native(location.description), + "city": to_native(location.city), + "country": to_native(location.country), + } + ) return tmp def get_locations(self): try: if self.module.params.get("id") is not None: - self.hcloud_location_info = [self.client.locations.get_by_id( - self.module.params.get("id") - )] + self.hcloud_location_info = [self.client.locations.get_by_id(self.module.params.get("id"))] elif self.module.params.get("name") is not None: - self.hcloud_location_info = [self.client.locations.get_by_name( - self.module.params.get("name") - )] + self.hcloud_location_info = [self.client.locations.get_by_name(self.module.params.get("name"))] else: self.hcloud_location_info = self.client.locations.get_all() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, name={"type": "str"}, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), supports_check_mode=True, ) def main(): - module = AnsibleHcloudLocationInfo.define_module() + module = AnsibleHCloudLocationInfo.define_module() + hcloud = AnsibleHCloudLocationInfo(module) - is_old_facts = module._name == 'hcloud_location_facts' - if is_old_facts: - module.deprecate("The 'hcloud_location_info' module has been renamed to 'hcloud_location_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudLocationInfo(module) hcloud.get_locations() result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_location_facts': result['hcloud_location_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_location_info': result['hcloud_location_info'] - } - module.exit_json(**ansible_info) + + ansible_info = {"hcloud_location_info": result["hcloud_location_info"]} + module.exit_json(**ansible_info) if __name__ == "__main__": diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_network.py b/ansible_collections/hetzner/hcloud/plugins/modules/network.py index 9c005d29f..24e45a48d 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_network.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/network.py @@ -1,16 +1,14 @@ #!/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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_network +module: network short_description: Create and manage cloud Networks on the Hetzner Cloud. @@ -38,6 +36,11 @@ options: - IP range of the Network. - Required if Network does not exist. type: str + expose_routes_to_vswitch: + description: + - Indicates if the routes from this network should be exposed to the vSwitch connection. + - The exposing only takes effect if a vSwitch connection is active. + type: bool labels: description: - User-defined labels (key-value pairs). @@ -53,23 +56,19 @@ options: choices: [ absent, present ] type: str -requirements: - - hcloud-python >= 1.3.0 - extends_documentation_fragment: - hetzner.hcloud.hcloud - -''' +""" EXAMPLES = """ - name: Create a basic network - hcloud_network: + hetzner.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: + hetzner.hcloud.network: name: my-network state: absent """ @@ -95,6 +94,11 @@ hcloud_network: type: str returned: always sample: 10.0.0.0/8 + expose_routes_to_vswitch: + description: Indicates if the routes from this network should be exposed to the vSwitch connection. + type: bool + returned: always + sample: false delete_protection: description: True if Network is protected for deletion type: bool @@ -111,20 +115,24 @@ hcloud_network: """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.networks import BoundNetwork -class AnsibleHcloudNetwork(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_network") - self.hcloud_network = None + +class AnsibleHCloudNetwork(AnsibleHCloud): + represent = "hcloud_network" + + hcloud_network: BoundNetwork | None = 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), + "expose_routes_to_vswitch": self.hcloud_network.expose_routes_to_vswitch, "delete_protection": self.hcloud_network.protection["delete"], "labels": self.hcloud_network.labels, } @@ -132,26 +140,24 @@ class AnsibleHcloudNetwork(Hcloud): 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") - ) + 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 Exception as e: - self.module.fail_json(msg=e.message) + self.hcloud_network = self.client.networks.get_by_name(self.module.params.get("name")) + except HCloudException as exception: + self.fail_json_hcloud(exception) def _create_network(self): - - self.module.fail_on_missing_params( - required_params=["name", "ip_range"] - ) + 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"), } + + expose_routes_to_vswitch = self.module.params.get("expose_routes_to_vswitch") + if expose_routes_to_vswitch is not None: + params["expose_routes_to_vswitch"] = expose_routes_to_vswitch + try: if not self.module.check_mode: self.client.networks.create(**params) @@ -160,8 +166,8 @@ class AnsibleHcloudNetwork(Hcloud): if delete_protection is not None: self._get_network() self.hcloud_network.change_protection(delete=delete_protection).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self._get_network() @@ -179,13 +185,22 @@ class AnsibleHcloudNetwork(Hcloud): self.hcloud_network.change_ip_range(ip_range=ip_range).wait_until_finished() self._mark_as_changed() + expose_routes_to_vswitch = self.module.params.get("expose_routes_to_vswitch") + if ( + expose_routes_to_vswitch is not None + and expose_routes_to_vswitch != self.hcloud_network.expose_routes_to_vswitch + ): + if not self.module.check_mode: + self.hcloud_network.update(expose_routes_to_vswitch=expose_routes_to_vswitch) + self._mark_as_changed() + delete_protection = self.module.params.get("delete_protection") if delete_protection is not None and delete_protection != self.hcloud_network.protection["delete"]: if not self.module.check_mode: self.hcloud_network.change_protection(delete=delete_protection).wait_until_finished() self._mark_as_changed() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._get_network() def present_network(self): @@ -202,34 +217,35 @@ class AnsibleHcloudNetwork(Hcloud): if not self.module.check_mode: self.client.networks.delete(self.hcloud_network) self._mark_as_changed() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self.hcloud_network = None - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, name={"type": "str"}, ip_range={"type": "str"}, + expose_routes_to_vswitch={"type": "bool"}, labels={"type": "dict"}, delete_protection={"type": "bool"}, state={ "choices": ["absent", "present"], "default": "present", }, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), - required_one_of=[['id', 'name']], + required_one_of=[["id", "name"]], supports_check_mode=True, ) def main(): - module = AnsibleHcloudNetwork.define_module() + module = AnsibleHCloudNetwork.define_module() - hcloud = AnsibleHcloudNetwork(module) + hcloud = AnsibleHCloudNetwork(module) state = module.params["state"] if state == "absent": hcloud.delete_network() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_network_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/network_info.py index 382e447aa..4008352b4 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_network_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/network_info.py @@ -1,16 +1,14 @@ #!/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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_network_info +module: network_info short_description: Gather info about your Hetzner Cloud networks. @@ -25,6 +23,7 @@ options: id: description: - The ID of the network you want to get. + - The module will fail if the provided ID is invalid. type: int name: description: @@ -37,7 +36,7 @@ options: extends_documentation_fragment: - hetzner.hcloud.hcloud -''' +""" EXAMPLES = """ - name: Gather hcloud network info @@ -110,6 +109,11 @@ hcloud_network_info: returned: always type: str sample: 10.0.0.1 + expose_routes_to_vswitch: + description: Indicates if the routes from this network should be exposed to the vSwitch connection. + returned: always + type: bool + sample: false servers: description: Servers attached to the network returned: always @@ -181,14 +185,17 @@ hcloud_network_info: """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.networks import BoundNetwork -class AnsibleHcloudNetworkInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_network_info") - self.hcloud_network_info = None +class AnsibleHCloudNetworkInfo(AnsibleHCloud): + represent = "hcloud_network_info" + + hcloud_network_info: list[BoundNetwork] | None = None def _prepare_result(self): tmp = [] @@ -206,10 +213,7 @@ class AnsibleHcloudNetworkInfo(Hcloud): subnets.append(prepared_subnet) routes = [] for route in network.routes: - prepared_route = { - "destination": route.destination, - "gateway": route.gateway - } + prepared_route = {"destination": route.destination, "gateway": route.gateway} routes.append(prepared_route) servers = [] @@ -233,59 +237,58 @@ class AnsibleHcloudNetworkInfo(Hcloud): } servers.append(prepared_server) - tmp.append({ - "id": to_native(network.id), - "name": to_native(network.name), - "ip_range": to_native(network.ip_range), - "subnetworks": subnets, - "routes": routes, - "servers": servers, - "labels": network.labels, - "delete_protection": network.protection["delete"], - }) + tmp.append( + { + "id": to_native(network.id), + "name": to_native(network.name), + "ip_range": to_native(network.ip_range), + "subnetworks": subnets, + "routes": routes, + "expose_routes_to_vswitch": network.expose_routes_to_vswitch, + "servers": servers, + "labels": network.labels, + "delete_protection": network.protection["delete"], + } + ) return tmp def get_networks(self): try: if self.module.params.get("id") is not None: - self.hcloud_network_info = [self.client.networks.get_by_id( - self.module.params.get("id") - )] + self.hcloud_network_info = [self.client.networks.get_by_id(self.module.params.get("id"))] elif self.module.params.get("name") is not None: - self.hcloud_network_info = [self.client.networks.get_by_name( - self.module.params.get("name") - )] + self.hcloud_network_info = [self.client.networks.get_by_name(self.module.params.get("name"))] elif self.module.params.get("label_selector") is not None: self.hcloud_network_info = self.client.networks.get_all( - label_selector=self.module.params.get("label_selector")) + label_selector=self.module.params.get("label_selector") + ) else: self.hcloud_network_info = self.client.networks.get_all() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, name={"type": "str"}, label_selector={"type": "str"}, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), supports_check_mode=True, ) def main(): - module = AnsibleHcloudNetworkInfo.define_module() + module = AnsibleHCloudNetworkInfo.define_module() + hcloud = AnsibleHCloudNetworkInfo(module) - hcloud = AnsibleHcloudNetworkInfo(module) hcloud.get_networks() result = hcloud.get_result() - info = { - 'hcloud_network_info': result['hcloud_network_info'] - } + + info = {"hcloud_network_info": result["hcloud_network_info"]} module.exit_json(**info) diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_placement_group.py b/ansible_collections/hetzner/hcloud/plugins/modules/placement_group.py index 522bb679d..ba26fad22 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_placement_group.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/placement_group.py @@ -1,16 +1,14 @@ #!/usr/bin/python -# -*- coding: utf-8 -*- # Copyright: (c) 2020, 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 +from __future__ import annotations DOCUMENTATION = """ --- -module: hcloud_placement_group +module: placement_group short_description: Create and manage placement groups on the Hetzner Cloud. @@ -47,31 +45,28 @@ options: choices: [ absent, present ] type: str -requirements: - - hcloud-python >= 1.15.0 - extends_documentation_fragment: - hetzner.hcloud.hcloud """ EXAMPLES = """ - name: Create a basic placement group - hcloud_placement_group: + hetzner.hcloud.placement_group: name: my-placement-group state: present type: spread - name: Create a placement group with labels - hcloud_placement_group: + hetzner.hcloud.placement_group: name: my-placement-group type: spread labels: - key: value - mylabel: 123 + key: value + mylabel: 123 state: present - name: Ensure the placement group is absent (remove if needed) - hcloud_placement_group: + hetzner.hcloud.placement_group: name: my-placement-group state: absent """ @@ -111,15 +106,18 @@ hcloud_placement_group: - 4712 """ -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native +from ansible.module_utils.common.text.converters import to_native +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.placement_groups import BoundPlacementGroup -class AnsibleHcloudPlacementGroup(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_placement_group") - self.hcloud_placement_group = None + +class AnsibleHCloudPlacementGroup(AnsibleHCloud): + represent = "hcloud_placement_group" + + hcloud_placement_group: BoundPlacementGroup | None = None def _prepare_result(self): return { @@ -133,20 +131,14 @@ class AnsibleHcloudPlacementGroup(Hcloud): def _get_placement_group(self): try: if self.module.params.get("id") is not None: - self.hcloud_placement_group = self.client.placement_groups.get_by_id( - self.module.params.get("id") - ) + self.hcloud_placement_group = self.client.placement_groups.get_by_id(self.module.params.get("id")) elif self.module.params.get("name") is not None: - self.hcloud_placement_group = self.client.placement_groups.get_by_name( - self.module.params.get("name") - ) - except Exception as e: - self.module.fail_json(msg=e.message) + self.hcloud_placement_group = self.client.placement_groups.get_by_name(self.module.params.get("name")) + except HCloudException as exception: + self.fail_json_hcloud(exception) def _create_placement_group(self): - self.module.fail_on_missing_params( - required_params=["name"] - ) + self.module.fail_on_missing_params(required_params=["name"]) params = { "name": self.module.params.get("name"), "type": self.module.params.get("type"), @@ -155,17 +147,15 @@ class AnsibleHcloudPlacementGroup(Hcloud): if not self.module.check_mode: try: self.client.placement_groups.create(**params) - except Exception as e: - self.module.fail_json(msg=e.message, **params) + except HCloudException as exception: + self.fail_json_hcloud(exception, params=params) self._mark_as_changed() self._get_placement_group() def _update_placement_group(self): name = self.module.params.get("name") if name is not None and self.hcloud_placement_group.name != name: - self.module.fail_on_missing_params( - required_params=["id"] - ) + self.module.fail_on_missing_params(required_params=["id"]) if not self.module.check_mode: self.hcloud_placement_group.update(name=name) self._mark_as_changed() @@ -193,8 +183,8 @@ class AnsibleHcloudPlacementGroup(Hcloud): self._mark_as_changed() self.hcloud_placement_group = None - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, @@ -205,18 +195,18 @@ class AnsibleHcloudPlacementGroup(Hcloud): "choices": ["absent", "present"], "default": "present", }, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), - required_one_of=[['id', 'name']], - required_if=[['state', 'present', ['name']]], + required_one_of=[["id", "name"]], + required_if=[["state", "present", ["name"]]], supports_check_mode=True, ) def main(): - module = AnsibleHcloudPlacementGroup.define_module() + module = AnsibleHCloudPlacementGroup.define_module() - hcloud = AnsibleHcloudPlacementGroup(module) + hcloud = AnsibleHCloudPlacementGroup(module) state = module.params.get("state") if state == "absent": hcloud.delete_placement_group() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_primary_ip.py b/ansible_collections/hetzner/hcloud/plugins/modules/primary_ip.py index c192d5fec..607f6c7e1 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_primary_ip.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/primary_ip.py @@ -1,16 +1,14 @@ #!/usr/bin/python -# -*- coding: utf-8 -*- # Copyright: (c) 2022, 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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_primary_ip +module: primary_ip short_description: Create and manage cloud Primary IPs on the Hetzner Cloud. @@ -63,29 +61,25 @@ options: choices: [ absent, present ] type: str -requirements: - - hcloud-python >= 1.9.0 - extends_documentation_fragment: - hetzner.hcloud.hcloud - -''' +""" EXAMPLES = """ - name: Create a basic IPv4 Primary IP - hcloud_primary_ip: + hetzner.hcloud.primary_ip: name: my-primary-ip datacenter: fsn1-dc14 type: ipv4 state: present - name: Create a basic IPv6 Primary IP - hcloud_primary_ip: + hetzner.hcloud.primary_ip: name: my-primary-ip datacenter: fsn1-dc14 type: ipv6 state: present - name: Primary IP should be absent - hcloud_primary_ip: + hetzner.hcloud.primary_ip: name: my-primary-ip state: absent """ @@ -136,14 +130,17 @@ hcloud_primary_ip: """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.primary_ips import BoundPrimaryIP -class AnsibleHcloudPrimaryIP(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_primary_ip") - self.hcloud_primary_ip = None + +class AnsibleHCloudPrimaryIP(AnsibleHCloud): + represent = "hcloud_primary_ip" + + hcloud_primary_ip: BoundPrimaryIP | None = None def _prepare_result(self): return { @@ -159,27 +156,19 @@ class AnsibleHcloudPrimaryIP(Hcloud): def _get_primary_ip(self): try: if self.module.params.get("id") is not None: - self.hcloud_primary_ip = self.client.primary_ips.get_by_id( - self.module.params.get("id") - ) + self.hcloud_primary_ip = self.client.primary_ips.get_by_id(self.module.params.get("id")) else: - self.hcloud_primary_ip = self.client.primary_ips.get_by_name( - self.module.params.get("name") - ) - except Exception as e: - self.module.fail_json(msg=e.message) + self.hcloud_primary_ip = self.client.primary_ips.get_by_name(self.module.params.get("name")) + except HCloudException as exception: + self.fail_json_hcloud(exception) def _create_primary_ip(self): - self.module.fail_on_missing_params( - required_params=["type", "datacenter"] - ) + self.module.fail_on_missing_params(required_params=["type", "datacenter"]) try: params = { "type": self.module.params.get("type"), "name": self.module.params.get("name"), - "datacenter": self.client.datacenters.get_by_name( - self.module.params.get("datacenter") - ) + "datacenter": self.client.datacenters.get_by_name(self.module.params.get("datacenter")), } if self.module.params.get("labels") is not None: @@ -191,8 +180,8 @@ class AnsibleHcloudPrimaryIP(Hcloud): delete_protection = self.module.params.get("delete_protection") if delete_protection is not None: self.hcloud_primary_ip.change_protection(delete=delete_protection).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self._get_primary_ip() @@ -211,8 +200,8 @@ class AnsibleHcloudPrimaryIP(Hcloud): self._mark_as_changed() self._get_primary_ip() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) def present_primary_ip(self): self._get_primary_ip() @@ -229,11 +218,11 @@ class AnsibleHcloudPrimaryIP(Hcloud): self.client.primary_ips.delete(self.hcloud_primary_ip) self._mark_as_changed() self.hcloud_primary_ip = None - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, @@ -247,17 +236,17 @@ class AnsibleHcloudPrimaryIP(Hcloud): "choices": ["absent", "present"], "default": "present", }, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), - required_one_of=[['id', 'name']], + required_one_of=[["id", "name"]], supports_check_mode=True, ) def main(): - module = AnsibleHcloudPrimaryIP.define_module() + module = AnsibleHCloudPrimaryIP.define_module() - hcloud = AnsibleHcloudPrimaryIP(module) + hcloud = AnsibleHCloudPrimaryIP(module) state = module.params["state"] if state == "absent": hcloud.delete_primary_ip() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/primary_ip_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/primary_ip_info.py new file mode 100644 index 000000000..c0bfdbb35 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/primary_ip_info.py @@ -0,0 +1,203 @@ +#!/usr/bin/python + +# 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 annotations + +DOCUMENTATION = """ +--- +module: primary_ip_info + +short_description: Gather infos about the Hetzner Cloud Primary IPs. + +description: + - Gather facts about your Hetzner Cloud Primary IPs. + +author: + - Lukas Kaemmerling (@LKaemmerling) + - Kevin Castner (@kcastner) + +options: + id: + description: + - The ID of the Primary IP you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name for the Primary IP you want to get. + type: str + label_selector: + description: + - The label selector for the Primary IP you want to get. + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Gather hcloud Primary IP infos + hetzner.hcloud.primary_ip_info: + register: output + +- name: Gather hcloud Primary IP infos by id + hetzner.hcloud.primary_ip_info: + id: 673954 + register: output + +- name: Gather hcloud Primary IP infos by name + hetzner.hcloud.primary_ip_info: + name: srv1-v4 + register: output + +- name: Gather hcloud Primary IP infos by label + hetzner.hcloud.primary_ip_info: + label_selector: srv03-ips + register: output + +- name: Print the gathered infos + debug: + var: output +""" + +RETURN = """ +hcloud_primary_ip_info: + description: The Primary IP infos as list + returned: always + type: complex + contains: + id: + description: Numeric identifier of the Primary IP + returned: always + type: int + sample: 1937415 + name: + description: Name of the Primary IP + returned: always + type: str + sample: my-primary-ip + ip: + description: IP address of the Primary IP + returned: always + type: str + sample: 131.232.99.1 + type: + description: Type of the Primary IP + returned: always + type: str + sample: ipv4 + assignee_id: + description: Numeric identifier of the ressource where the Primary IP is assigned to. + returned: always + type: int + sample: 19584637 + assignee_type: + description: Name of the type where the Primary IP is assigned to. + returned: always + type: str + sample: server + home_location: + description: Location with datacenter where the Primary IP was created in + returned: always + type: str + sample: fsn1-dc1 + dns_ptr: + description: Shows the DNS PTR Record for Primary IP. + returned: always + type: str + sample: srv01.example.com + labels: + description: User-defined labels (key-value pairs) + returned: always + type: dict + delete_protection: + description: True if the Primary IP is protected for deletion + returned: always + type: bool +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.primary_ips import BoundPrimaryIP + + +class AnsibleHCloudPrimaryIPInfo(AnsibleHCloud): + represent = "hcloud_primary_ip_info" + + hcloud_primary_ip_info: list[BoundPrimaryIP] | None = None + + def _prepare_result(self): + tmp = [] + + for primary_ip in self.hcloud_primary_ip_info: + if primary_ip is not None: + dns_ptr = None + if len(primary_ip.dns_ptr) > 0: + dns_ptr = primary_ip.dns_ptr[0]["dns_ptr"] + tmp.append( + { + "id": to_native(primary_ip.id), + "name": to_native(primary_ip.name), + "ip": to_native(primary_ip.ip), + "type": to_native(primary_ip.type), + "assignee_id": ( + to_native(primary_ip.assignee_id) if primary_ip.assignee_id is not None else None + ), + "assignee_type": to_native(primary_ip.assignee_type), + "home_location": to_native(primary_ip.datacenter.name), + "dns_ptr": to_native(dns_ptr) if dns_ptr is not None else None, + "labels": primary_ip.labels, + "delete_protection": primary_ip.protection["delete"], + } + ) + + return tmp + + def get_primary_ips(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_primary_ip_info = [self.client.primary_ips.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None: + self.hcloud_primary_ip_info = [self.client.primary_ips.get_by_name(self.module.params.get("name"))] + elif self.module.params.get("label_selector") is not None: + self.hcloud_primary_ip_info = self.client.primary_ips.get_all( + label_selector=self.module.params.get("label_selector") + ) + else: + self.hcloud_primary_ip_info = self.client.primary_ips.get_all() + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + label_selector={"type": "str"}, + name={"type": "str"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudPrimaryIPInfo.define_module() + hcloud = AnsibleHCloudPrimaryIPInfo(module) + + hcloud.get_primary_ips() + result = hcloud.get_result() + + ansible_info = {"hcloud_primary_ip_info": result["hcloud_primary_ip_info"]} + module.exit_json(**ansible_info) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_rdns.py b/ansible_collections/hetzner/hcloud/plugins/modules/rdns.py index 9f79fbe70..b2decdec8 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_rdns.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/rdns.py @@ -1,16 +1,14 @@ #!/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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_rdns +module: rdns short_description: Create and manage reverse DNS entries on the Hetzner Cloud. @@ -24,19 +22,19 @@ author: options: server: description: - - The name of the Hetzner Cloud server you want to add the reverse DNS entry to. + - Name or ID of the Hetzner Cloud server you want to add the reverse DNS entry to. type: str floating_ip: description: - - The name of the Hetzner Cloud Floating IP you want to add the reverse DNS entry to. + - Name or ID of the Hetzner Cloud Floating IP you want to add the reverse DNS entry to. type: str primary_ip: description: - - The name of the Hetzner Cloud Primary IP you want to add the reverse DNS entry to. + - Name or ID of the Hetzner Cloud Primary IP you want to add the reverse DNS entry to. type: str load_balancer: description: - - The name of the Hetzner Cloud Load Balancer you want to add the reverse DNS entry to. + - Name or ID of the Hetzner Cloud Load Balancer you want to add the reverse DNS entry to. type: str ip_address: description: @@ -55,45 +53,41 @@ options: choices: [ absent, present ] type: str -requirements: - - hcloud-python >= 1.3.0 - extends_documentation_fragment: - hetzner.hcloud.hcloud - -''' +""" EXAMPLES = """ - name: Create a reverse DNS entry for a server - hcloud_rdns: + hetzner.hcloud.rdns: server: my-server ip_address: 123.123.123.123 dns_ptr: example.com state: present - name: Create a reverse DNS entry for a Floating IP - hcloud_rdns: + hetzner.hcloud.rdns: floating_ip: my-floating-ip ip_address: 123.123.123.123 dns_ptr: example.com state: present - name: Create a reverse DNS entry for a Primary IP - hcloud_rdns: + hetzner.hcloud.rdns: primary_ip: my-primary-ip ip_address: 123.123.123.123 dns_ptr: example.com state: present - name: Create a reverse DNS entry for a Load Balancer - hcloud_rdns: + hetzner.hcloud.rdns: load_balancer: my-load-balancer ip_address: 123.123.123.123 dns_ptr: example.com state: present - name: Ensure the reverse DNS entry is absent (remove if needed) - hcloud_rdns: + hetzner.hcloud.rdns: server: my-server ip_address: 123.123.123.123 dns_ptr: example.com @@ -138,17 +132,25 @@ hcloud_rdns: sample: example.com """ +import ipaddress +from typing import Any + from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils +from ansible.module_utils.common.text.converters import to_native +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.floating_ips import BoundFloatingIP +from ..module_utils.vendor.hcloud.load_balancers import BoundLoadBalancer +from ..module_utils.vendor.hcloud.primary_ips import BoundPrimaryIP +from ..module_utils.vendor.hcloud.servers import BoundServer -class AnsibleHcloudReverseDNS(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_rdns") - self.hcloud_resource = None - self.hcloud_rdns = None + +class AnsibleHCloudReverseDNS(AnsibleHCloud): + represent = "hcloud_rdns" + + hcloud_resource: BoundServer | BoundFloatingIP | BoundLoadBalancer | BoundPrimaryIP | None = None + hcloud_rdns: dict[str, Any] | None = None def _prepare_result(self): result = { @@ -172,35 +174,37 @@ class AnsibleHcloudReverseDNS(Hcloud): def _get_resource(self): try: if self.module.params.get("server"): - self.hcloud_resource = self.client.servers.get_by_name( - self.module.params.get("server") + self.hcloud_resource = self._client_get_by_name_or_id( + "servers", + self.module.params.get("server"), ) - if self.hcloud_resource is None: - self.module.fail_json(msg="The selected server does not exist") elif self.module.params.get("floating_ip"): - self.hcloud_resource = self.client.floating_ips.get_by_name( - self.module.params.get("floating_ip") + self.hcloud_resource = self._client_get_by_name_or_id( + "floating_ips", + self.module.params.get("floating_ip"), ) - if self.hcloud_resource is None: - self.module.fail_json(msg="The selected Floating IP does not exist") elif self.module.params.get("primary_ip"): - self.hcloud_resource = self.client.primary_ips.get_by_name( - self.module.params.get("primary_ip") + self.hcloud_resource = self._client_get_by_name_or_id( + "primary_ips", + self.module.params.get("primary_ip"), ) - if self.hcloud_resource is None: - self.module.fail_json(msg="The selected Floating IP does not exist") elif self.module.params.get("load_balancer"): - self.hcloud_resource = self.client.load_balancers.get_by_name( - self.module.params.get("load_balancer") + self.hcloud_resource = self._client_get_by_name_or_id( + "load_balancers", + self.module.params.get("load_balancer"), ) - if self.hcloud_resource is None: - self.module.fail_json(msg="The selected Load Balancer does not exist") - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) def _get_rdns(self): ip_address = self.module.params.get("ip_address") - if utils.validate_ip_address(ip_address): + + try: + ip_address_obj = ipaddress.ip_address(ip_address) + except ValueError: + self.module.fail_json(msg=f"The given IP address is not valid: {ip_address}") + + if ip_address_obj.version == 4: if self.module.params.get("server"): if self.hcloud_resource.public_net.ipv4.ip == ip_address: self.hcloud_rdns = { @@ -234,7 +238,7 @@ class AnsibleHcloudReverseDNS(Hcloud): else: self.module.fail_json(msg="The selected Load Balancer does not have this IP address") - elif utils.validate_ip_v6_address(ip_address): + elif ip_address_obj.version == 6: if self.module.params.get("server"): for ipv6_address_dns_ptr in self.hcloud_resource.public_net.ipv6.dns_ptr: if ipv6_address_dns_ptr["ip"] == ip_address: @@ -263,13 +267,9 @@ class AnsibleHcloudReverseDNS(Hcloud): "ip_address": ipv6_address_dns_ptr["ip"], "dns_ptr": ipv6_address_dns_ptr["dns_ptr"], } - else: - self.module.fail_json(msg="The given IP address is not valid") def _create_rdns(self): - self.module.fail_on_missing_params( - required_params=["dns_ptr"] - ) + self.module.fail_on_missing_params(required_params=["dns_ptr"]) params = { "ip": self.module.params.get("ip_address"), "dns_ptr": self.module.params.get("dns_ptr"), @@ -278,8 +278,8 @@ class AnsibleHcloudReverseDNS(Hcloud): if not self.module.check_mode: try: self.hcloud_resource.change_dns_ptr(**params).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self._get_resource() self._get_rdns() @@ -295,8 +295,8 @@ class AnsibleHcloudReverseDNS(Hcloud): if not self.module.check_mode: try: self.hcloud_resource.change_dns_ptr(**params).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self._get_resource() self._get_rdns() @@ -315,14 +315,14 @@ class AnsibleHcloudReverseDNS(Hcloud): if self.hcloud_rdns is not None: if not self.module.check_mode: try: - self.hcloud_resource.change_dns_ptr(ip=self.hcloud_rdns['ip_address'], dns_ptr=None) - except Exception as e: - self.module.fail_json(msg=e.message) + self.hcloud_resource.change_dns_ptr(ip=self.hcloud_rdns["ip_address"], dns_ptr=None) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self.hcloud_rdns = None - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( server={"type": "str"}, @@ -335,18 +335,18 @@ class AnsibleHcloudReverseDNS(Hcloud): "choices": ["absent", "present"], "default": "present", }, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), - required_one_of=[['server', 'floating_ip', 'load_balancer', 'primary_ip']], - mutually_exclusive=[["server", "floating_ip", 'load_balancer', 'primary_ip']], + required_one_of=[["server", "floating_ip", "load_balancer", "primary_ip"]], + mutually_exclusive=[["server", "floating_ip", "load_balancer", "primary_ip"]], supports_check_mode=True, ) def main(): - module = AnsibleHcloudReverseDNS.define_module() + module = AnsibleHCloudReverseDNS.define_module() - hcloud = AnsibleHcloudReverseDNS(module) + hcloud = AnsibleHCloudReverseDNS(module) state = module.params["state"] if state == "absent": hcloud.delete_rdns() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_route.py b/ansible_collections/hetzner/hcloud/plugins/modules/route.py index c75177953..3c96a7382 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_route.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/route.py @@ -1,16 +1,14 @@ #!/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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_route +module: route short_description: Create and delete cloud routes on the Hetzner Cloud. @@ -24,7 +22,7 @@ author: options: network: description: - - The name of the Hetzner Cloud Network. + - Name or ID of the Hetzner Cloud Network. type: str required: true destination: @@ -44,24 +42,20 @@ options: choices: [ absent, present ] type: str -requirements: - - hcloud-python >= 1.3.0 - extends_documentation_fragment: - hetzner.hcloud.hcloud - -''' +""" EXAMPLES = """ - name: Create a basic route - hcloud_route: + hetzner.hcloud.route: network: my-network destination: 10.100.1.0/24 gateway: 10.0.1.1 state: present - name: Ensure the route is absent - hcloud_route: + hetzner.hcloud.route: network: my-network destination: 10.100.1.0/24 gateway: 10.0.1.1 @@ -92,20 +86,18 @@ hcloud_route: """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native -try: - from hcloud.networks.domain import NetworkRoute -except ImportError: - NetworkRoute = None +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.networks import BoundNetwork, NetworkRoute -class AnsibleHcloudRoute(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_route") - self.hcloud_network = None - self.hcloud_route = None +class AnsibleHCloudRoute(AnsibleHCloud): + represent = "hcloud_route" + + hcloud_network: BoundNetwork | None = None + hcloud_route: NetworkRoute | None = None def _prepare_result(self): return { @@ -116,10 +108,13 @@ class AnsibleHcloudRoute(Hcloud): def _get_network(self): try: - self.hcloud_network = self.client.networks.get_by_name(self.module.params.get("network")) + self.hcloud_network = self._client_get_by_name_or_id( + "networks", + self.module.params.get("network"), + ) self.hcloud_route = None - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) def _get_route(self): destination = self.module.params.get("destination") @@ -130,15 +125,14 @@ class AnsibleHcloudRoute(Hcloud): def _create_route(self): route = NetworkRoute( - destination=self.module.params.get("destination"), - gateway=self.module.params.get('gateway') + destination=self.module.params.get("destination"), gateway=self.module.params.get("gateway") ) if not self.module.check_mode: try: self.hcloud_network.add_route(route=route).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self._get_network() @@ -157,13 +151,13 @@ class AnsibleHcloudRoute(Hcloud): if not self.module.check_mode: try: self.hcloud_network.delete_route(self.hcloud_route).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self.hcloud_route = None - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( network={"type": "str", "required": True}, @@ -173,16 +167,16 @@ class AnsibleHcloudRoute(Hcloud): "choices": ["absent", "present"], "default": "present", }, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), supports_check_mode=True, ) def main(): - module = AnsibleHcloudRoute.define_module() + module = AnsibleHCloudRoute.define_module() - hcloud = AnsibleHcloudRoute(module) + hcloud = AnsibleHCloudRoute(module) state = module.params["state"] if state == "absent": hcloud.delete_route() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server.py b/ansible_collections/hetzner/hcloud/plugins/modules/server.py index 3a77da695..f5cadb807 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/server.py @@ -1,16 +1,14 @@ #!/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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_server +module: server short_description: Create and manage cloud servers on the Hetzner Cloud. @@ -67,7 +65,7 @@ options: datacenter: description: - Datacenter of Server. - - Required of no I(location) is given and server does not exist. + - Required if no I(location) is given and server does not exist. type: str backups: description: @@ -78,17 +76,17 @@ options: - Resize the disk size, when resizing a server. - If you want to downgrade the server later, this value should be False. type: bool - default: no + default: false enable_ipv4: description: - Enables the public ipv4 address type: bool - default: yes + default: true enable_ipv6: description: - Enables the public ipv6 address type: bool - default: yes + default: true ipv4: description: - ID of the ipv4 Primary IP to use. If omitted and enable_ipv4 is true, a new ipv4 Primary IP will automatically be created @@ -110,18 +108,17 @@ options: - Force the upgrade of the server. - Power off the server if it is running on upgrade. type: bool - default: no force: description: - Force the update of the server. - May power off the server if update. type: bool - default: no + default: false allow_deprecated_image: description: - Allows the creation of servers with deprecated images. type: bool - default: no + default: false user_data: description: - User Data to be passed to the server on creation. @@ -158,80 +155,80 @@ options: extends_documentation_fragment: - hetzner.hcloud.hcloud -''' +""" EXAMPLES = """ - name: Create a basic server - hcloud_server: + hetzner.hcloud.server: name: my-server server_type: cx11 - image: ubuntu-18.04 + image: ubuntu-22.04 state: present - name: Create a basic server with ssh key - hcloud_server: + hetzner.hcloud.server: name: my-server server_type: cx11 - image: ubuntu-18.04 + image: ubuntu-22.04 location: fsn1 ssh_keys: - me@myorganisation state: present - name: Resize an existing server - hcloud_server: + hetzner.hcloud.server: name: my-server server_type: cx21 - upgrade_disk: yes + upgrade_disk: true state: present - name: Ensure the server is absent (remove if needed) - hcloud_server: + hetzner.hcloud.server: name: my-server state: absent - name: Ensure the server is started - hcloud_server: + hetzner.hcloud.server: name: my-server state: started - name: Ensure the server is stopped - hcloud_server: + hetzner.hcloud.server: name: my-server state: stopped - name: Ensure the server is restarted - hcloud_server: + hetzner.hcloud.server: name: my-server state: restarted - name: Ensure the server is will be booted in rescue mode and therefore restarted - hcloud_server: + hetzner.hcloud.server: name: my-server rescue_mode: linux64 state: restarted - name: Ensure the server is rebuild - hcloud_server: + hetzner.hcloud.server: name: my-server - image: ubuntu-18.04 + image: ubuntu-22.04 state: rebuild - name: Add server to placement group - hcloud_server: + hetzner.hcloud.server: name: my-server placement_group: my-placement-group - force: True + force: true state: present - name: Remove server from placement group - hcloud_server: + hetzner.hcloud.server: name: my-server - placement_group: null + placement_group: state: present - name: Add server with private network only - hcloud_server: + hetzner.hcloud.server: name: my-server enable_ipv4: false enable_ipv6: false @@ -257,6 +254,11 @@ hcloud_server: returned: always type: str sample: my-server + created: + description: Point in time when the Server was created (in ISO-8601 format) + returned: always + type: str + sample: "2023-11-06T13:36:56+00:00" status: description: Status of the server returned: always @@ -333,44 +335,50 @@ hcloud_server: version_added: "0.1.0" """ +from datetime import datetime, timedelta, timezone + from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud -from datetime import timedelta - -try: - from hcloud.volumes.domain import Volume - from hcloud.ssh_keys.domain import SSHKey - from hcloud.servers.domain import Server, ServerCreatePublicNetwork - from hcloud.firewalls.domain import FirewallResource -except ImportError: - Volume = None - SSHKey = None - Server = None - ServerCreatePublicNetwork = None - FirewallResource = None - - -class AnsibleHcloudServer(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_server") - self.hcloud_server = None +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.firewalls import FirewallResource +from ..module_utils.vendor.hcloud.servers import ( + BoundServer, + Server, + ServerCreatePublicNetwork, +) +from ..module_utils.vendor.hcloud.ssh_keys import SSHKey +from ..module_utils.vendor.hcloud.volumes import Volume + + +class AnsibleHCloudServer(AnsibleHCloud): + represent = "hcloud_server" + + hcloud_server: BoundServer | None = None def _prepare_result(self): image = None if self.hcloud_server.image is None else to_native(self.hcloud_server.image.name) - placement_group = None if self.hcloud_server.placement_group is None else to_native( - self.hcloud_server.placement_group.name) - ipv4_address = None if self.hcloud_server.public_net.ipv4 is None else to_native( - self.hcloud_server.public_net.ipv4.ip) + placement_group = ( + None if self.hcloud_server.placement_group is None else to_native(self.hcloud_server.placement_group.name) + ) + ipv4_address = ( + None if self.hcloud_server.public_net.ipv4 is None else to_native(self.hcloud_server.public_net.ipv4.ip) + ) ipv6 = None if self.hcloud_server.public_net.ipv6 is None else to_native(self.hcloud_server.public_net.ipv6.ip) - backup_window = None if self.hcloud_server.backup_window is None else to_native(self.hcloud_server.backup_window) + backup_window = ( + None if self.hcloud_server.backup_window is None else to_native(self.hcloud_server.backup_window) + ) return { "id": to_native(self.hcloud_server.id), "name": to_native(self.hcloud_server.name), + "created": to_native(self.hcloud_server.created.isoformat()), "ipv4_address": ipv4_address, "ipv6": ipv6, "private_networks": [to_native(net.network.name) for net in self.hcloud_server.private_net], - "private_networks_info": [{"name": to_native(net.network.name), "ip": net.ip} for net in self.hcloud_server.private_net], + "private_networks_info": [ + {"name": to_native(net.network.name), "ip": net.ip} for net in self.hcloud_server.private_net + ], "image": image, "server_type": to_native(self.hcloud_server.server_type.name), "datacenter": to_native(self.hcloud_server.datacenter.name), @@ -387,20 +395,14 @@ class AnsibleHcloudServer(Hcloud): def _get_server(self): try: if self.module.params.get("id") is not None: - self.hcloud_server = self.client.servers.get_by_id( - self.module.params.get("id") - ) + self.hcloud_server = self.client.servers.get_by_id(self.module.params.get("id")) else: - self.hcloud_server = self.client.servers.get_by_name( - self.module.params.get("name") - ) - except Exception as e: - self.module.fail_json(msg=e.message) + self.hcloud_server = self.client.servers.get_by_name(self.module.params.get("name")) + except HCloudException as exception: + self.fail_json_hcloud(exception) def _create_server(self): - self.module.fail_on_missing_params( - required_params=["name", "server_type", "image"] - ) + self.module.fail_on_missing_params(required_params=["name", "server_type", "image"]) server_type = self._get_server_type() @@ -413,21 +415,21 @@ class AnsibleHcloudServer(Hcloud): "placement_group": self._get_placement_group(), "public_net": ServerCreatePublicNetwork( enable_ipv4=self.module.params.get("enable_ipv4"), - enable_ipv6=self.module.params.get("enable_ipv6") - ) + enable_ipv6=self.module.params.get("enable_ipv6"), + ), } if self.module.params.get("ipv4") is not None: - p = self.client.primary_ips.get_by_name(self.module.params.get("ipv4")) - if not p: - p = self.client.primary_ips.get_by_id(self.module.params.get("ipv4")) - params["public_net"].ipv4 = p + primary_ip = self.client.primary_ips.get_by_name(self.module.params.get("ipv4")) + if not primary_ip: + primary_ip = self.client.primary_ips.get_by_id(self.module.params.get("ipv4")) + params["public_net"].ipv4 = primary_ip if self.module.params.get("ipv6") is not None: - p = self.client.primary_ips.get_by_name(self.module.params.get("ipv6")) - if not p: - p = self.client.primary_ips.get_by_id(self.module.params.get("ipv6")) - params["public_net"].ipv6 = p + primary_ip = self.client.primary_ips.get_by_name(self.module.params.get("ipv6")) + if not primary_ip: + primary_ip = self.client.primary_ips.get_by_id(self.module.params.get("ipv6")) + params["public_net"].ipv6 = primary_ip if self.module.params.get("private_networks") is not None: _networks = [] @@ -439,37 +441,28 @@ class AnsibleHcloudServer(Hcloud): params["networks"] = _networks if self.module.params.get("ssh_keys") is not None: - params["ssh_keys"] = [ - SSHKey(name=ssh_key_name) - for ssh_key_name in self.module.params.get("ssh_keys") - ] + params["ssh_keys"] = [SSHKey(name=ssh_key_name) for ssh_key_name in self.module.params.get("ssh_keys")] if self.module.params.get("volumes") is not None: - params["volumes"] = [ - Volume(id=volume_id) for volume_id in self.module.params.get("volumes") - ] + params["volumes"] = [Volume(id=volume_id) for volume_id in self.module.params.get("volumes")] if self.module.params.get("firewalls") is not None: params["firewalls"] = [] - for fw in self.module.params.get("firewalls"): - f = self.client.firewalls.get_by_name(fw) - if f is not None: + for firewall_param in self.module.params.get("firewalls"): + firewall = self.client.firewalls.get_by_name(firewall_param) + if firewall is not None: # When firewall name is not available look for id instead - params["firewalls"].append(f) + params["firewalls"].append(firewall) else: - params["firewalls"].append(self.client.firewalls.get_by_id(fw)) + params["firewalls"].append(self.client.firewalls.get_by_id(firewall_param)) if self.module.params.get("location") is None and self.module.params.get("datacenter") is None: # When not given, the API will choose the location. params["location"] = None params["datacenter"] = None elif self.module.params.get("location") is not None and self.module.params.get("datacenter") is None: - params["location"] = self.client.locations.get_by_name( - self.module.params.get("location") - ) + params["location"] = self.client.locations.get_by_name(self.module.params.get("location")) elif self.module.params.get("location") is None and self.module.params.get("datacenter") is not None: - params["datacenter"] = self.client.datacenters.get_by_name( - self.module.params.get("datacenter") - ) + params["datacenter"] = self.client.datacenters.get_by_name(self.module.params.get("datacenter")) if self.module.params.get("state") == "stopped": params["start_after_create"] = False @@ -494,16 +487,22 @@ class AnsibleHcloudServer(Hcloud): rebuild_protection = self.module.params.get("rebuild_protection") if delete_protection is not None and rebuild_protection is not None: self._get_server() - self.hcloud_server.change_protection(delete=delete_protection, - rebuild=rebuild_protection).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) + self.hcloud_server.change_protection( + delete=delete_protection, + rebuild=rebuild_protection, + ).wait_until_finished() + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self._get_server() def _get_image(self, server_type): - image_resp = self.client.images.get_list(name=self.module.params.get("image"), architecture=server_type.architecture, include_deprecated=True) - images = getattr(image_resp, 'images') + image_resp = self.client.images.get_list( + name=self.module.params.get("image"), + architecture=server_type.architecture, + include_deprecated=True, + ) + images = getattr(image_resp, "images") image = None if images is not None and len(images) > 0: # If image name is not available look for id instead @@ -511,46 +510,76 @@ class AnsibleHcloudServer(Hcloud): else: try: image = self.client.images.get_by_id(self.module.params.get("image")) - except Exception: - self.module.fail_json(msg="Image %s was not found" % self.module.params.get('image')) + except HCloudException as exception: + self.fail_json_hcloud(exception, msg=f"Image {self.module.params.get('image')} was not found") if image.deprecated is not None: available_until = image.deprecated + timedelta(days=90) if self.module.params.get("allow_deprecated_image"): self.module.warn( - "You try to use a deprecated image. The image %s will continue to be available until %s.") % ( - image.name, available_until.strftime('%Y-%m-%d')) + f"You try to use a deprecated image. The image {image.name} will " + f"continue to be available until {available_until.strftime('%Y-%m-%d')}." + ) else: self.module.fail_json( - msg=("You try to use a deprecated image. The image %s will continue to be available until %s." + - " If you want to use this image use allow_deprecated_image=yes." - ) % (image.name, available_until.strftime('%Y-%m-%d'))) + msg=( + f"You try to use a deprecated image. The image {image.name} will " + f"continue to be available until {available_until.strftime('%Y-%m-%d')}. " + "If you want to use this image use allow_deprecated_image=true." + ) + ) return image def _get_server_type(self): - server_type = self.client.server_types.get_by_name( - self.module.params.get("server_type") - ) + server_type = self.client.server_types.get_by_name(self.module.params.get("server_type")) if server_type is None: try: server_type = self.client.server_types.get_by_id(self.module.params.get("server_type")) - except Exception: - self.module.fail_json(msg="server_type %s was not found" % self.module.params.get('server_type')) + except HCloudException as exception: + self.fail_json_hcloud( + exception, + msg=f"server_type {self.module.params.get('server_type')} was not found", + ) + + self._check_and_warn_deprecated_server(server_type) return server_type + def _check_and_warn_deprecated_server(self, server_type): + if server_type.deprecation is None: + return + + if server_type.deprecation.unavailable_after < datetime.now(timezone.utc): + self.module.warn( + f"Attention: The server plan {server_type.name} is deprecated and can " + "no longer be ordered. Existing servers of that plan will continue to " + "work as before and no action is required on your part. " + "It is possible to migrate this server to another server plan by setting " + "the server_type parameter on the hetzner.hcloud.server module." + ) + else: + server_type_unavailable_date = server_type.deprecation.unavailable_after.strftime("%Y-%m-%d") + self.module.warn( + f"Attention: The server plan {server_type.name} is deprecated and will " + f"no longer be available for order as of {server_type_unavailable_date}. " + "Existing servers of that plan will continue to work as before and no " + "action is required on your part. " + "It is possible to migrate this server to another server plan by setting " + "the server_type parameter on the hetzner.hcloud.server module." + ) + def _get_placement_group(self): if self.module.params.get("placement_group") is None: return None - placement_group = self.client.placement_groups.get_by_name( - self.module.params.get("placement_group") - ) + placement_group = self.client.placement_groups.get_by_name(self.module.params.get("placement_group")) if placement_group is None: try: placement_group = self.client.placement_groups.get_by_id(self.module.params.get("placement_group")) - except Exception: - self.module.fail_json( - msg="placement_group %s was not found" % self.module.params.get("placement_group")) + except HCloudException as exception: + self.fail_json_hcloud( + exception, + msg=f"placement_group {self.module.params.get('placement_group')} was not found", + ) return placement_group @@ -558,20 +587,17 @@ class AnsibleHcloudServer(Hcloud): if self.module.params.get(field) is None: return None - primary_ip = self.client.primary_ips.get_by_name( - self.module.params.get(field) - ) + primary_ip = self.client.primary_ips.get_by_name(self.module.params.get(field)) if primary_ip is None: try: primary_ip = self.client.primary_ips.get_by_id(self.module.params.get(field)) - except Exception as e: - self.module.fail_json( - msg="primary_ip %s was not found" % self.module.params.get(field)) + except HCloudException as exception: + self.fail_json_hcloud(exception, msg=f"primary_ip {self.module.params.get(field)} was not found") return primary_ip def _update_server(self): - if "force_upgrade" in self.module.params: + if "force_upgrade" in self.module.params and self.module.params.get("force_upgrade") is not None: self.module.warn("force_upgrade is deprecated, use force instead") try: @@ -609,30 +635,35 @@ class AnsibleHcloudServer(Hcloud): for current_firewall in self.hcloud_server.public_net.firewalls: if current_firewall.firewall.name not in wanted_firewalls: self._mark_as_changed() - if not self.module.check_mode: - r = FirewallResource(type="server", server=self.hcloud_server) - actions = self.client.firewalls.remove_from_resources(current_firewall.firewall, [r]) - for a in actions: - a.wait_until_finished() + if self.module.check_mode: + continue + + firewall_resource = FirewallResource(type="server", server=self.hcloud_server) + actions = self.client.firewalls.remove_from_resources( + current_firewall.firewall, + [firewall_resource], + ) + for action in actions: + action.wait_until_finished() # Adding wanted firewalls that doesn't exist yet - for fname in wanted_firewalls: + for firewall_name in wanted_firewalls: found = False - for f in self.hcloud_server.public_net.firewalls: - if f.firewall.name == fname: + for firewall in self.hcloud_server.public_net.firewalls: + if firewall.firewall.name == firewall_name: found = True break if not found: self._mark_as_changed() if not self.module.check_mode: - fw = self.client.firewalls.get_by_name(fname) - if fw is None: - self.module.fail_json(msg="firewall %s was not found" % fname) - r = FirewallResource(type="server", server=self.hcloud_server) - actions = self.client.firewalls.apply_to_resources(fw, [r]) - for a in actions: - a.wait_until_finished() + firewall = self.client.firewalls.get_by_name(firewall_name) + if firewall is None: + self.module.fail_json(msg=f"firewall {firewall_name} was not found") + firewall_resource = FirewallResource(type="server", server=self.hcloud_server) + actions = self.client.firewalls.apply_to_resources(firewall, [firewall_resource]) + for action in actions: + action.wait_until_finished() if "placement_group" in self.module.params: if self.module.params["placement_group"] is None and self.hcloud_server.placement_group is not None: @@ -641,12 +672,9 @@ class AnsibleHcloudServer(Hcloud): self._mark_as_changed() else: placement_group = self._get_placement_group() - if ( - placement_group is not None and - ( - self.hcloud_server.placement_group is None or - self.hcloud_server.placement_group.id != placement_group.id - ) + if placement_group is not None and ( + self.hcloud_server.placement_group is None + or self.hcloud_server.placement_group.id != placement_group.id ): self.stop_server_if_forced() if not self.module.check_mode: @@ -655,9 +683,9 @@ class AnsibleHcloudServer(Hcloud): if "ipv4" in self.module.params: if ( - self.module.params["ipv4"] is None and - self.hcloud_server.public_net.primary_ipv4 is not None and - not self.module.params.get("enable_ipv4") + self.module.params["ipv4"] is None + and self.hcloud_server.public_net.primary_ipv4 is not None + and not self.module.params.get("enable_ipv4") ): self.stop_server_if_forced() if not self.module.check_mode: @@ -665,12 +693,9 @@ class AnsibleHcloudServer(Hcloud): self._mark_as_changed() else: primary_ip = self._get_primary_ip("ipv4") - if ( - primary_ip is not None and - ( - self.hcloud_server.public_net.primary_ipv4 is None or - self.hcloud_server.public_net.primary_ipv4.id != primary_ip.id - ) + if primary_ip is not None and ( + self.hcloud_server.public_net.primary_ipv4 is None + or self.hcloud_server.public_net.primary_ipv4.id != primary_ip.id ): self.stop_server_if_forced() if not self.module.check_mode: @@ -680,9 +705,9 @@ class AnsibleHcloudServer(Hcloud): self._mark_as_changed() if "ipv6" in self.module.params: if ( - (self.module.params["ipv6"] is None or self.module.params["ipv6"] == "") and - self.hcloud_server.public_net.primary_ipv6 is not None and - not self.module.params.get("enable_ipv6") + (self.module.params["ipv6"] is None or self.module.params["ipv6"] == "") + and self.hcloud_server.public_net.primary_ipv6 is not None + and not self.module.params.get("enable_ipv6") ): self.stop_server_if_forced() if not self.module.check_mode: @@ -690,12 +715,9 @@ class AnsibleHcloudServer(Hcloud): self._mark_as_changed() else: primary_ip = self._get_primary_ip("ipv6") - if ( - primary_ip is not None and - ( - self.hcloud_server.public_net.primary_ipv6 is None or - self.hcloud_server.public_net.primary_ipv6.id != primary_ip.id - ) + if primary_ip is not None and ( + self.hcloud_server.public_net.primary_ipv6 is None + or self.hcloud_server.public_net.primary_ipv6.id != primary_ip.id ): self.stop_server_if_forced() if not self.module.check_mode: @@ -710,11 +732,10 @@ class AnsibleHcloudServer(Hcloud): else: _networks = {} for network_name_or_id in self.module.params.get("private_networks"): - _found_network = self.client.networks.get_by_name(network_name_or_id) \ - or self.client.networks.get_by_id(network_name_or_id) - _networks.update( - {_found_network.id: _found_network} - ) + _found_network = self.client.networks.get_by_name( + network_name_or_id + ) or self.client.networks.get_by_id(network_name_or_id) + _networks.update({_found_network.id: _found_network}) networks_target = _networks networks_is = dict() for p_network in self.hcloud_server.private_net: @@ -732,53 +753,56 @@ class AnsibleHcloudServer(Hcloud): self._mark_as_changed() server_type = self.module.params.get("server_type") - if server_type is not None and self.hcloud_server.server_type.name != server_type: - self.stop_server_if_forced() - - timeout = 100 - if self.module.params.get("upgrade_disk"): - timeout = ( - 1000 - ) # When we upgrade the disk to the resize progress takes some more time. - if not self.module.check_mode: - self.hcloud_server.change_type( - server_type=self._get_server_type(), - upgrade_disk=self.module.params.get("upgrade_disk"), - ).wait_until_finished(timeout) - self._mark_as_changed() + if server_type is not None: + if self.hcloud_server.server_type.name == server_type: + # Check if we should warn for using an deprecated server type + self._check_and_warn_deprecated_server(self.hcloud_server.server_type) - if ( - not self.module.check_mode and - ( - ( - self.module.params.get("state") == "present" and - previous_server_status == Server.STATUS_RUNNING - ) or - self.module.params.get("state") == "started" - ) + else: + # Server type should be changed + self.stop_server_if_forced() + + timeout = 100 + if self.module.params.get("upgrade_disk"): + timeout = 1000 # When we upgrade the disk to the resize progress takes some more time. + if not self.module.check_mode: + self.hcloud_server.change_type( + server_type=self._get_server_type(), + upgrade_disk=self.module.params.get("upgrade_disk"), + ).wait_until_finished(timeout) + self._mark_as_changed() + + if not self.module.check_mode and ( + (self.module.params.get("state") == "present" and previous_server_status == Server.STATUS_RUNNING) + or self.module.params.get("state") == "started" ): self.start_server() delete_protection = self.module.params.get("delete_protection") rebuild_protection = self.module.params.get("rebuild_protection") if (delete_protection is not None and rebuild_protection is not None) and ( - delete_protection != self.hcloud_server.protection["delete"] or rebuild_protection != - self.hcloud_server.protection["rebuild"]): + delete_protection != self.hcloud_server.protection["delete"] + or rebuild_protection != self.hcloud_server.protection["rebuild"] + ): if not self.module.check_mode: - self.hcloud_server.change_protection(delete=delete_protection, - rebuild=rebuild_protection).wait_until_finished() + self.hcloud_server.change_protection( + delete=delete_protection, + rebuild=rebuild_protection, + ).wait_until_finished() self._mark_as_changed() self._get_server() - except Exception as e: - self.module.fail_json(msg=e) + except HCloudException as exception: + self.fail_json_hcloud(exception) def _set_rescue_mode(self, rescue_mode): if self.module.params.get("ssh_keys"): - resp = self.hcloud_server.enable_rescue(type=rescue_mode, - ssh_keys=[self.client.ssh_keys.get_by_name(ssh_key_name).id - for - ssh_key_name in - self.module.params.get("ssh_keys")]) + resp = self.hcloud_server.enable_rescue( + type=rescue_mode, + ssh_keys=[ + self.client.ssh_keys.get_by_name(ssh_key_name).id + for ssh_key_name in self.module.params.get("ssh_keys") + ], + ) else: resp = self.hcloud_server.enable_rescue(type=rescue_mode) resp.action.wait_until_finished() @@ -792,8 +816,8 @@ class AnsibleHcloudServer(Hcloud): self.client.servers.power_on(self.hcloud_server).wait_until_finished() self._mark_as_changed() self._get_server() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) def stop_server(self): try: @@ -803,40 +827,40 @@ class AnsibleHcloudServer(Hcloud): self.client.servers.power_off(self.hcloud_server).wait_until_finished() self._mark_as_changed() self._get_server() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) def stop_server_if_forced(self): previous_server_status = self.hcloud_server.status if previous_server_status == Server.STATUS_RUNNING and not self.module.check_mode: if ( - self.module.params.get("force_upgrade") or - self.module.params.get("force") or - self.module.params.get("state") == "stopped" + self.module.params.get("force_upgrade") + or self.module.params.get("force") + or self.module.params.get("state") == "stopped" ): self.stop_server() # Only stopped server can be upgraded return previous_server_status else: self.module.warn( - "You can not upgrade a running instance %s. You need to stop the instance or use force=yes." - % self.hcloud_server.name + f"You can not upgrade a running instance {self.hcloud_server.name}. " + "You need to stop the instance or use force=true." ) return None def rebuild_server(self): - self.module.fail_on_missing_params( - required_params=["image"] - ) + self.module.fail_on_missing_params(required_params=["image"]) try: if not self.module.check_mode: image = self._get_image(self.hcloud_server.server_type) - self.client.servers.rebuild(self.hcloud_server, image).wait_until_finished(1000) # When we rebuild the server progress takes some more time. + # When we rebuild the server progress takes some more time. + resp = self.client.servers.rebuild(self.hcloud_server, image, return_response=True) + resp.action.wait_until_finished(1000) self._mark_as_changed() self._get_server() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) def present_server(self): self._get_server() @@ -853,11 +877,11 @@ class AnsibleHcloudServer(Hcloud): self.client.servers.delete(self.hcloud_server).wait_until_finished() self._mark_as_changed() self.hcloud_server = None - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, @@ -879,7 +903,7 @@ class AnsibleHcloudServer(Hcloud): ipv6={"type": "str"}, private_networks={"type": "list", "elements": "str", "default": None}, force={"type": "bool", "default": False}, - force_upgrade={"type": "bool", "default": False}, + force_upgrade={"type": "bool"}, allow_deprecated_image={"type": "bool", "default": False}, rescue_mode={"type": "str"}, delete_protection={"type": "bool"}, @@ -889,9 +913,9 @@ class AnsibleHcloudServer(Hcloud): "choices": ["absent", "present", "restarted", "started", "stopped", "rebuild"], "default": "present", }, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), - required_one_of=[['id', 'name']], + required_one_of=[["id", "name"]], mutually_exclusive=[["location", "datacenter"]], required_together=[["delete_protection", "rebuild_protection"]], supports_check_mode=True, @@ -899,9 +923,9 @@ class AnsibleHcloudServer(Hcloud): def main(): - module = AnsibleHcloudServer.define_module() + module = AnsibleHCloudServer.define_module() - hcloud = AnsibleHcloudServer(module) + hcloud = AnsibleHCloudServer(module) state = module.params.get("state") if state == "absent": hcloud.delete_server() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/server_info.py index 102ceec0d..cee1634cb 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/server_info.py @@ -1,24 +1,20 @@ #!/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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_server_info +module: server_info short_description: Gather infos about your Hetzner Cloud servers. description: - Gather infos about your Hetzner Cloud servers. - - This module was called C(hcloud_server_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_server_facts). - Note that the M(hetzner.hcloud.hcloud_server_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_server_info)! author: - Lukas Kaemmerling (@LKaemmerling) @@ -27,6 +23,7 @@ options: id: description: - The ID of the server you want to get. + - The module will fail if the provided ID is invalid. type: int name: description: @@ -39,11 +36,11 @@ options: extends_documentation_fragment: - hetzner.hcloud.hcloud -''' +""" EXAMPLES = """ - name: Gather hcloud server infos - hcloud_server_info: + hetzner.hcloud.server_info: register: output - name: Print the gathered infos @@ -67,6 +64,11 @@ hcloud_server_info: returned: always type: str sample: my-server + created: + description: Point in time when the Server was created (in ISO-8601 format) + returned: always + type: str + sample: "2023-11-06T13:36:56+00:00" status: description: Status of the server returned: always @@ -144,14 +146,17 @@ hcloud_server_info: """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.servers import BoundServer -class AnsibleHcloudServerInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_server_info") - self.hcloud_server_info = None + +class AnsibleHCloudServerInfo(AnsibleHCloud): + represent = "hcloud_server_info" + + hcloud_server_info: list[BoundServer] | None = None def _prepare_result(self): tmp = [] @@ -163,81 +168,70 @@ class AnsibleHcloudServerInfo(Hcloud): ipv4_address = None if server.public_net.ipv4 is None else to_native(server.public_net.ipv4.ip) ipv6 = None if server.public_net.ipv6 is None else to_native(server.public_net.ipv6.ip) backup_window = None if server.backup_window is None else to_native(server.backup_window) - tmp.append({ - "id": to_native(server.id), - "name": to_native(server.name), - "ipv4_address": ipv4_address, - "ipv6": ipv6, - "private_networks": [to_native(net.network.name) for net in server.private_net], - "private_networks_info": [{"name": to_native(net.network.name), "ip": net.ip} for net in server.private_net], - "image": image, - "server_type": to_native(server.server_type.name), - "datacenter": to_native(server.datacenter.name), - "location": to_native(server.datacenter.location.name), - "placement_group": placement_group, - "rescue_enabled": server.rescue_enabled, - "backup_window": backup_window, - "labels": server.labels, - "status": to_native(server.status), - "delete_protection": server.protection["delete"], - "rebuild_protection": server.protection["rebuild"], - }) + tmp.append( + { + "id": to_native(server.id), + "name": to_native(server.name), + "created": to_native(server.created.isoformat()), + "ipv4_address": ipv4_address, + "ipv6": ipv6, + "private_networks": [to_native(net.network.name) for net in server.private_net], + "private_networks_info": [ + {"name": to_native(net.network.name), "ip": net.ip} for net in server.private_net + ], + "image": image, + "server_type": to_native(server.server_type.name), + "datacenter": to_native(server.datacenter.name), + "location": to_native(server.datacenter.location.name), + "placement_group": placement_group, + "rescue_enabled": server.rescue_enabled, + "backup_window": backup_window, + "labels": server.labels, + "status": to_native(server.status), + "delete_protection": server.protection["delete"], + "rebuild_protection": server.protection["rebuild"], + } + ) return tmp def get_servers(self): try: if self.module.params.get("id") is not None: - self.hcloud_server_info = [self.client.servers.get_by_id( - self.module.params.get("id") - )] + self.hcloud_server_info = [self.client.servers.get_by_id(self.module.params.get("id"))] elif self.module.params.get("name") is not None: - self.hcloud_server_info = [self.client.servers.get_by_name( - self.module.params.get("name") - )] + self.hcloud_server_info = [self.client.servers.get_by_name(self.module.params.get("name"))] elif self.module.params.get("label_selector") is not None: self.hcloud_server_info = self.client.servers.get_all( - label_selector=self.module.params.get("label_selector")) + label_selector=self.module.params.get("label_selector") + ) else: self.hcloud_server_info = self.client.servers.get_all() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, name={"type": "str"}, label_selector={"type": "str"}, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), supports_check_mode=True, ) def main(): - module = AnsibleHcloudServerInfo.define_module() - - is_old_facts = module._name == 'hcloud_server_facts' - if is_old_facts: - module.deprecate("The 'hcloud_server_facts' module has been renamed to 'hcloud_server_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") + module = AnsibleHCloudServerInfo.define_module() + hcloud = AnsibleHCloudServerInfo(module) - hcloud = AnsibleHcloudServerInfo(module) hcloud.get_servers() result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_server_facts': result['hcloud_server_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_server_info': result['hcloud_server_info'] - } - module.exit_json(**ansible_info) + ansible_info = {"hcloud_server_info": result["hcloud_server_info"]} + module.exit_json(**ansible_info) if __name__ == "__main__": diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_network.py b/ansible_collections/hetzner/hcloud/plugins/modules/server_network.py index 79f6838fd..ca80a8a76 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_network.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/server_network.py @@ -1,16 +1,14 @@ #!/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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_server_network +module: server_network short_description: Manage the relationship between Hetzner Cloud Networks and servers @@ -24,12 +22,12 @@ author: options: network: description: - - The name of the Hetzner Cloud Networks. + - Name or ID of the Hetzner Cloud Networks. type: str required: true server: description: - - The name of the Hetzner Cloud server. + - Name or ID of the Hetzner Cloud server. type: str required: true ip: @@ -48,40 +46,36 @@ options: choices: [ absent, present ] type: str -requirements: - - hcloud-python >= 1.3.0 - extends_documentation_fragment: - hetzner.hcloud.hcloud - -''' +""" EXAMPLES = """ - name: Create a basic server network - hcloud_server_network: + hetzner.hcloud.server_network: network: my-network server: my-server state: present - name: Create a server network and specify the ip address - hcloud_server_network: + hetzner.hcloud.server_network: network: my-network server: my-server ip: 10.0.0.1 state: present - name: Create a server network and add alias ips - hcloud_server_network: + hetzner.hcloud.server_network: network: my-network server: my-server ip: 10.0.0.1 alias_ips: - - 10.1.0.1 - - 10.2.0.1 + - 10.1.0.1 + - 10.2.0.1 state: present - name: Ensure the server network is absent (remove if needed) - hcloud_server_network: + hetzner.hcloud.server_network: network: my-network server: my-server state: absent @@ -110,27 +104,27 @@ hcloud_server_network: sample: 10.0.0.8 alias_ips: description: Alias IPs of the server within the Network ip range - type: str + type: list + elements: str returned: always sample: [10.1.0.1, ...] """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native -try: - from hcloud import APIException -except ImportError: - APIException = None +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import APIException, HCloudException +from ..module_utils.vendor.hcloud.networks import BoundNetwork +from ..module_utils.vendor.hcloud.servers import BoundServer, PrivateNet -class AnsibleHcloudServerNetwork(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_server_network") - self.hcloud_network = None - self.hcloud_server = None - self.hcloud_server_network = None +class AnsibleHCloudServerNetwork(AnsibleHCloud): + represent = "hcloud_server_network" + + hcloud_network: BoundNetwork | None = None + hcloud_server: BoundServer | None = None + hcloud_server_network: PrivateNet | None = None def _prepare_result(self): return { @@ -142,20 +136,26 @@ class AnsibleHcloudServerNetwork(Hcloud): def _get_server_and_network(self): try: - self.hcloud_network = self.client.networks.get_by_name(self.module.params.get("network")) - self.hcloud_server = self.client.servers.get_by_name(self.module.params.get("server")) + self.hcloud_network = self._client_get_by_name_or_id( + "networks", + self.module.params.get("network"), + ) + self.hcloud_server = self._client_get_by_name_or_id( + "servers", + self.module.params.get("server"), + ) self.hcloud_server_network = None - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) def _get_server_network(self): - for privateNet in self.hcloud_server.private_net: - if privateNet.network.id == self.hcloud_network.id: - self.hcloud_server_network = privateNet + for private_net in self.hcloud_server.private_net: + if private_net.network.id == self.hcloud_network.id: + self.hcloud_server_network = private_net def _create_server_network(self): params = { - "network": self.hcloud_network + "network": self.hcloud_network, } if self.module.params.get("ip") is not None: @@ -166,8 +166,8 @@ class AnsibleHcloudServerNetwork(Hcloud): if not self.module.check_mode: try: self.hcloud_server.attach_to_network(**params).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self._get_server_and_network() @@ -175,7 +175,7 @@ class AnsibleHcloudServerNetwork(Hcloud): def _update_server_network(self): params = { - "network": self.hcloud_network + "network": self.hcloud_network, } alias_ips = self.module.params.get("alias_ips") if alias_ips is not None and sorted(self.hcloud_server_network.alias_ips) != sorted(alias_ips): @@ -184,8 +184,8 @@ class AnsibleHcloudServerNetwork(Hcloud): if not self.module.check_mode: try: self.hcloud_server.change_alias_ips(**params).wait_until_finished() - except APIException as e: - self.module.fail_json(msg=e.message) + except APIException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self._get_server_and_network() @@ -206,13 +206,13 @@ class AnsibleHcloudServerNetwork(Hcloud): if not self.module.check_mode: try: self.hcloud_server.detach_from_network(self.hcloud_server_network.network).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self.hcloud_server_network = None - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( network={"type": "str", "required": True}, @@ -223,16 +223,16 @@ class AnsibleHcloudServerNetwork(Hcloud): "choices": ["absent", "present"], "default": "present", }, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), supports_check_mode=True, ) def main(): - module = AnsibleHcloudServerNetwork.define_module() + module = AnsibleHCloudServerNetwork.define_module() - hcloud = AnsibleHcloudServerNetwork(module) + hcloud = AnsibleHCloudServerNetwork(module) state = module.params["state"] if state == "absent": hcloud.delete_server_network() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/server_type_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/server_type_info.py new file mode 100644 index 000000000..61f1f5011 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/server_type_info.py @@ -0,0 +1,204 @@ +#!/usr/bin/python + +# 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 annotations + +DOCUMENTATION = """ +--- +module: server_type_info + +short_description: Gather infos about the Hetzner Cloud server types. + + +description: + - Gather infos about your Hetzner Cloud server types. + +author: + - Lukas Kaemmerling (@LKaemmerling) + +options: + id: + description: + - The ID of the server type you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name of the server type you want to get. + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Gather hcloud server type infos + hetzner.hcloud.server_type_info: + register: output + +- name: Print the gathered infos + debug: + var: output.hcloud_server_type_info +""" + +RETURN = """ +hcloud_server_type_info: + description: The server type infos as list + returned: always + type: complex + contains: + id: + description: Numeric identifier of the server type + returned: always + type: int + sample: 1937415 + name: + description: Name of the server type + returned: always + type: str + sample: fsn1 + description: + description: Detail description of the server type + returned: always + type: str + sample: Falkenstein DC Park 1 + cores: + description: Number of cpu cores a server of this type will have + returned: always + type: int + sample: 1 + memory: + description: Memory a server of this type will have in GB + returned: always + type: int + sample: 1 + disk: + description: Disk size a server of this type will have in GB + returned: always + type: int + sample: 25 + storage_type: + description: Type of server boot drive + returned: always + type: str + sample: local + cpu_type: + description: Type of cpu + returned: always + type: str + sample: shared + architecture: + description: Architecture of cpu + returned: always + type: str + sample: x86 + included_traffic: + description: Free traffic per month in bytes + returned: always + type: int + sample: 21990232555520 + deprecation: + description: | + Describes if, when & how the resources was deprecated. + If this field is set to None the resource is not deprecated. If it has a value, it is considered deprecated. + returned: success + type: dict + contains: + announced: + description: Date of when the deprecation was announced. + returned: success + type: str + sample: "2021-11-09T09:00:00+00:00" + unavailable_after: + description: | + After the time in this field, the resource will not be available from the general listing + endpoint of the resource type, and it can not be used in new resources. For example, if this is + an image, you can not create new servers with this image after the mentioned date. + returned: success + type: str + sample: "2021-12-01T00:00:00+00:00" + +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.server_types import BoundServerType + + +class AnsibleHCloudServerTypeInfo(AnsibleHCloud): + represent = "hcloud_server_type_info" + + hcloud_server_type_info: list[BoundServerType] | None = None + + def _prepare_result(self): + tmp = [] + + for server_type in self.hcloud_server_type_info: + if server_type is not None: + tmp.append( + { + "id": to_native(server_type.id), + "name": to_native(server_type.name), + "description": to_native(server_type.description), + "cores": server_type.cores, + "memory": server_type.memory, + "disk": server_type.disk, + "storage_type": to_native(server_type.storage_type), + "cpu_type": to_native(server_type.cpu_type), + "architecture": to_native(server_type.architecture), + "included_traffic": server_type.included_traffic, + "deprecation": ( + { + "announced": server_type.deprecation.announced.isoformat(), + "unavailable_after": server_type.deprecation.unavailable_after.isoformat(), + } + if server_type.deprecation is not None + else None + ), + } + ) + return tmp + + def get_server_types(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_server_type_info = [self.client.server_types.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None: + self.hcloud_server_type_info = [self.client.server_types.get_by_name(self.module.params.get("name"))] + else: + self.hcloud_server_type_info = self.client.server_types.get_all() + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudServerTypeInfo.define_module() + hcloud = AnsibleHCloudServerTypeInfo(module) + + hcloud.get_server_types() + result = hcloud.get_result() + + ansible_info = {"hcloud_server_type_info": result["hcloud_server_type_info"]} + module.exit_json(**ansible_info) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_ssh_key.py b/ansible_collections/hetzner/hcloud/plugins/modules/ssh_key.py index 59a5197f5..349c52c68 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_ssh_key.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/ssh_key.py @@ -1,16 +1,14 @@ #!/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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_ssh_key +module: ssh_key short_description: Create and manage ssh keys on the Hetzner Cloud. @@ -55,26 +53,26 @@ options: extends_documentation_fragment: - hetzner.hcloud.hcloud -''' +""" EXAMPLES = """ - name: Create a basic ssh_key - hcloud_ssh_key: + hetzner.hcloud.ssh_key: name: my-ssh_key - public_key: "ssh-rsa AAAjjk76kgf...Xt" + public_key: ssh-rsa AAAjjk76kgf...Xt state: present - name: Create a ssh_key with labels - hcloud_ssh_key: + hetzner.hcloud.ssh_key: name: my-ssh_key - public_key: "ssh-rsa AAAjjk76kgf...Xt" + public_key: ssh-rsa AAAjjk76kgf...Xt labels: - key: value - mylabel: 123 + key: value + mylabel: 123 state: present - name: Ensure the ssh_key is absent (remove if needed) - hcloud_ssh_key: + hetzner.hcloud.ssh_key: name: my-ssh_key state: absent """ @@ -115,14 +113,17 @@ hcloud_ssh_key: """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.ssh_keys import BoundSSHKey -class AnsibleHcloudSSHKey(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_ssh_key") - self.hcloud_ssh_key = None + +class AnsibleHCloudSSHKey(AnsibleHCloud): + represent = "hcloud_ssh_key" + + hcloud_ssh_key: BoundSSHKey | None = None def _prepare_result(self): return { @@ -136,45 +137,35 @@ class AnsibleHcloudSSHKey(Hcloud): def _get_ssh_key(self): try: if self.module.params.get("id") is not None: - self.hcloud_ssh_key = self.client.ssh_keys.get_by_id( - self.module.params.get("id") - ) + self.hcloud_ssh_key = self.client.ssh_keys.get_by_id(self.module.params.get("id")) elif self.module.params.get("fingerprint") is not None: - self.hcloud_ssh_key = self.client.ssh_keys.get_by_fingerprint( - self.module.params.get("fingerprint") - ) + self.hcloud_ssh_key = self.client.ssh_keys.get_by_fingerprint(self.module.params.get("fingerprint")) elif self.module.params.get("name") is not None: - self.hcloud_ssh_key = self.client.ssh_keys.get_by_name( - self.module.params.get("name") - ) + self.hcloud_ssh_key = self.client.ssh_keys.get_by_name(self.module.params.get("name")) - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) def _create_ssh_key(self): - self.module.fail_on_missing_params( - required_params=["name", "public_key"] - ) + self.module.fail_on_missing_params(required_params=["name", "public_key"]) params = { "name": self.module.params.get("name"), "public_key": self.module.params.get("public_key"), - "labels": self.module.params.get("labels") + "labels": self.module.params.get("labels"), } if not self.module.check_mode: try: self.client.ssh_keys.create(**params) - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self._get_ssh_key() def _update_ssh_key(self): name = self.module.params.get("name") if name is not None and self.hcloud_ssh_key.name != name: - self.module.fail_on_missing_params( - required_params=["id"] - ) + self.module.fail_on_missing_params(required_params=["id"]) if not self.module.check_mode: self.hcloud_ssh_key.update(name=name) self._mark_as_changed() @@ -200,13 +191,13 @@ class AnsibleHcloudSSHKey(Hcloud): if not self.module.check_mode: try: self.client.ssh_keys.delete(self.hcloud_ssh_key) - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self.hcloud_ssh_key = None - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, @@ -218,18 +209,18 @@ class AnsibleHcloudSSHKey(Hcloud): "choices": ["absent", "present"], "default": "present", }, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), - required_one_of=[['id', 'name', 'fingerprint']], - required_if=[['state', 'present', ['name']]], + required_one_of=[["id", "name", "fingerprint"]], + required_if=[["state", "present", ["name"]]], supports_check_mode=True, ) def main(): - module = AnsibleHcloudSSHKey.define_module() + module = AnsibleHCloudSSHKey.define_module() - hcloud = AnsibleHcloudSSHKey(module) + hcloud = AnsibleHCloudSSHKey(module) state = module.params.get("state") if state == "absent": hcloud.delete_ssh_key() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_ssh_key_facts.py b/ansible_collections/hetzner/hcloud/plugins/modules/ssh_key_info.py index aab98ed60..7a4ab5928 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_ssh_key_facts.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/ssh_key_info.py @@ -1,27 +1,24 @@ #!/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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_ssh_key_info +module: ssh_key_info short_description: Gather infos about your Hetzner Cloud ssh_keys. description: - Gather facts about your Hetzner Cloud ssh_keys. - - This module was called C(hcloud_ssh_key_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_ssh_key_facts). - Note that the M(hetzner.hcloud.hcloud_ssh_key_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_ssh_key_info)! author: - Christopher Schmitt (@cschmitt-hcloud) options: id: description: - The ID of the ssh key you want to get. + - The module will fail if the provided ID is invalid. type: int name: description: @@ -38,11 +35,11 @@ options: extends_documentation_fragment: - hetzner.hcloud.hcloud -''' +""" EXAMPLES = """ - name: Gather hcloud sshkey infos - hcloud_ssh_key_info: + hetzner.hcloud.ssh_key_info: register: output - name: Print the gathered infos debug: @@ -80,89 +77,79 @@ hcloud_ssh_key_info: returned: always type: dict """ + from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.ssh_keys import BoundSSHKey -class AnsibleHcloudSSHKeyInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_ssh_key_info") - self.hcloud_ssh_key_info = None +class AnsibleHCloudSSHKeyInfo(AnsibleHCloud): + represent = "hcloud_ssh_key_info" + + hcloud_ssh_key_info: list[BoundSSHKey] | None = None def _prepare_result(self): ssh_keys = [] for ssh_key in self.hcloud_ssh_key_info: if ssh_key: - ssh_keys.append({ - "id": to_native(ssh_key.id), - "name": to_native(ssh_key.name), - "fingerprint": to_native(ssh_key.fingerprint), - "public_key": to_native(ssh_key.public_key), - "labels": ssh_key.labels - }) + ssh_keys.append( + { + "id": to_native(ssh_key.id), + "name": to_native(ssh_key.name), + "fingerprint": to_native(ssh_key.fingerprint), + "public_key": to_native(ssh_key.public_key), + "labels": ssh_key.labels, + } + ) return ssh_keys def get_ssh_keys(self): try: if self.module.params.get("id") is not None: - self.hcloud_ssh_key_info = [self.client.ssh_keys.get_by_id( - self.module.params.get("id") - )] + self.hcloud_ssh_key_info = [self.client.ssh_keys.get_by_id(self.module.params.get("id"))] elif self.module.params.get("name") is not None: - self.hcloud_ssh_key_info = [self.client.ssh_keys.get_by_name( - self.module.params.get("name") - )] + self.hcloud_ssh_key_info = [self.client.ssh_keys.get_by_name(self.module.params.get("name"))] elif self.module.params.get("fingerprint") is not None: - self.hcloud_ssh_key_info = [self.client.ssh_keys.get_by_fingerprint( - self.module.params.get("fingerprint") - )] + self.hcloud_ssh_key_info = [ + self.client.ssh_keys.get_by_fingerprint(self.module.params.get("fingerprint")) + ] elif self.module.params.get("label_selector") is not None: self.hcloud_ssh_key_info = self.client.ssh_keys.get_all( - label_selector=self.module.params.get("label_selector")) + label_selector=self.module.params.get("label_selector") + ) else: self.hcloud_ssh_key_info = self.client.ssh_keys.get_all() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, name={"type": "str"}, fingerprint={"type": "str"}, label_selector={"type": "str"}, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), supports_check_mode=True, ) def main(): - module = AnsibleHcloudSSHKeyInfo.define_module() - - is_old_facts = module._name == 'hcloud_ssh_key_facts' - if is_old_facts: - module.deprecate("The 'hcloud_ssh_key_facts' module has been renamed to 'hcloud_ssh_key_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") + module = AnsibleHCloudSSHKeyInfo.define_module() + hcloud = AnsibleHCloudSSHKeyInfo(module) - hcloud = AnsibleHcloudSSHKeyInfo(module) hcloud.get_ssh_keys() result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_ssh_key_facts': result['hcloud_ssh_key_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_ssh_key_info': result['hcloud_ssh_key_info'] - } - module.exit_json(**ansible_info) + ansible_info = {"hcloud_ssh_key_info": result["hcloud_ssh_key_info"]} + module.exit_json(**ansible_info) if __name__ == "__main__": diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_subnetwork.py b/ansible_collections/hetzner/hcloud/plugins/modules/subnetwork.py index c2ba66d80..aea40bb13 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_subnetwork.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/subnetwork.py @@ -1,16 +1,14 @@ #!/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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_subnetwork +module: subnetwork short_description: Manage cloud subnetworks on the Hetzner Cloud. @@ -24,7 +22,7 @@ author: options: network: description: - - The ID or Name of the Hetzner Cloud Networks. + - The name or ID of the Hetzner Cloud Networks. type: str required: true ip_range: @@ -55,17 +53,13 @@ options: choices: [ absent, present ] type: str -requirements: - - hcloud-python >= 1.10.0 - extends_documentation_fragment: - hetzner.hcloud.hcloud - -''' +""" EXAMPLES = """ - name: Create a basic subnetwork - hcloud_subnetwork: + hetzner.hcloud.subnetwork: network: my-network ip_range: 10.0.0.0/16 network_zone: eu-central @@ -73,7 +67,7 @@ EXAMPLES = """ state: present - name: Create a basic subnetwork - hcloud_subnetwork: + hetzner.hcloud.subnetwork: network: my-vswitch-network ip_range: 10.0.0.0/24 network_zone: eu-central @@ -82,7 +76,7 @@ EXAMPLES = """ state: present - name: Ensure the subnetwork is absent (remove if needed) - hcloud_subnetwork: + hetzner.hcloud.subnetwork: network: my-network ip_range: 10.0.0.0/8 network_zone: eu-central @@ -129,20 +123,18 @@ hcloud_subnetwork: """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native -try: - from hcloud.networks.domain import NetworkSubnet -except ImportError: - NetworkSubnet = None +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.networks import BoundNetwork, NetworkSubnet -class AnsibleHcloudSubnetwork(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_subnetwork") - self.hcloud_network = None - self.hcloud_subnetwork = None +class AnsibleHCloudSubnetwork(AnsibleHCloud): + represent = "hcloud_subnetwork" + + hcloud_network: BoundNetwork | None = None + hcloud_subnetwork: NetworkSubnet | None = None def _prepare_result(self): return { @@ -156,10 +148,13 @@ class AnsibleHcloudSubnetwork(Hcloud): def _get_network(self): try: - self.hcloud_network = self.client.networks.get_by_name(self.module.params.get("network")) + self.hcloud_network = self._client_get_by_name_or_id( + "networks", + self.module.params.get("network"), + ) self.hcloud_subnetwork = None - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) def _get_subnetwork(self): subnet_ip_range = self.module.params.get("ip_range") @@ -170,20 +165,18 @@ class AnsibleHcloudSubnetwork(Hcloud): def _create_subnetwork(self): params = { "ip_range": self.module.params.get("ip_range"), - "type": self.module.params.get('type'), - "network_zone": self.module.params.get('network_zone') + "type": self.module.params.get("type"), + "network_zone": self.module.params.get("network_zone"), } - if self.module.params.get('type') == NetworkSubnet.TYPE_VSWITCH: - self.module.fail_on_missing_params( - required_params=["vswitch_id"] - ) - params["vswitch_id"] = self.module.params.get('vswitch_id') + if self.module.params.get("type") == NetworkSubnet.TYPE_VSWITCH: + self.module.fail_on_missing_params(required_params=["vswitch_id"]) + params["vswitch_id"] = self.module.params.get("vswitch_id") if not self.module.check_mode: try: self.hcloud_network.add_subnet(subnet=NetworkSubnet(**params)).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self._get_network() @@ -202,38 +195,34 @@ class AnsibleHcloudSubnetwork(Hcloud): if not self.module.check_mode: try: self.hcloud_network.delete_subnet(self.hcloud_subnetwork).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self.hcloud_subnetwork = None - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( network={"type": "str", "required": True}, network_zone={"type": "str", "required": True}, - type={ - "type": "str", - "required": True, - "choices": ["server", "cloud", "vswitch"] - }, + type={"type": "str", "required": True, "choices": ["server", "cloud", "vswitch"]}, ip_range={"type": "str", "required": True}, vswitch_id={"type": "int"}, state={ "choices": ["absent", "present"], "default": "present", }, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), supports_check_mode=True, ) def main(): - module = AnsibleHcloudSubnetwork.define_module() + module = AnsibleHCloudSubnetwork.define_module() - hcloud = AnsibleHcloudSubnetwork(module) + hcloud = AnsibleHCloudSubnetwork(module) state = module.params["state"] if state == "absent": hcloud.delete_subnetwork() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_volume.py b/ansible_collections/hetzner/hcloud/plugins/modules/volume.py index 623a399b4..8442ed90b 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_volume.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/volume.py @@ -1,16 +1,14 @@ #!/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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_volume +module: volume short_description: Create and manage block Volume on the Hetzner Cloud. @@ -75,36 +73,36 @@ options: extends_documentation_fragment: - hetzner.hcloud.hcloud -''' +""" EXAMPLES = """ - name: Create a Volume - hcloud_volume: + hetzner.hcloud.volume: name: my-volume location: fsn1 size: 100 state: present - name: Create a Volume and format it with ext4 - hcloud_volume: + hetzner.hcloud.volume: name: my-volume location: fsn format: ext4 size: 100 state: present - name: Mount a existing Volume and automount - hcloud_volume: + hetzner.hcloud.volume: name: my-volume server: my-server - automount: yes + automount: true state: present - name: Mount a existing Volume and automount - hcloud_volume: + hetzner.hcloud.volume: name: my-volume server: my-server - automount: yes + automount: true state: present - name: Ensure the Volume is absent (remove if needed) - hcloud_volume: + hetzner.hcloud.volume: name: my-volume state: absent """ @@ -162,14 +160,17 @@ hcloud_volume: """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.volumes import BoundVolume + +class AnsibleHCloudVolume(AnsibleHCloud): + represent = "hcloud_volume" -class AnsibleHcloudVolume(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_volume") - self.hcloud_volume = None + hcloud_volume: BoundVolume | None = None def _prepare_result(self): server_name = None @@ -190,31 +191,25 @@ class AnsibleHcloudVolume(Hcloud): def _get_volume(self): try: if self.module.params.get("id") is not None: - self.hcloud_volume = self.client.volumes.get_by_id( - self.module.params.get("id") - ) + self.hcloud_volume = self.client.volumes.get_by_id(self.module.params.get("id")) else: - self.hcloud_volume = self.client.volumes.get_by_name( - self.module.params.get("name") - ) - except Exception as e: - self.module.fail_json(msg=e.message) + self.hcloud_volume = self.client.volumes.get_by_name(self.module.params.get("name")) + except HCloudException as exception: + self.fail_json_hcloud(exception) def _create_volume(self): - self.module.fail_on_missing_params( - required_params=["name", "size"] - ) + self.module.fail_on_missing_params(required_params=["name", "size"]) params = { "name": self.module.params.get("name"), "size": self.module.params.get("size"), "automount": self.module.params.get("automount"), "format": self.module.params.get("format"), - "labels": self.module.params.get("labels") + "labels": self.module.params.get("labels"), } if self.module.params.get("server") is not None: - params['server'] = self.client.servers.get_by_name(self.module.params.get("server")) + params["server"] = self.client.servers.get_by_name(self.module.params.get("server")) elif self.module.params.get("location") is not None: - params['location'] = self.client.locations.get_by_name(self.module.params.get("location")) + params["location"] = self.client.locations.get_by_name(self.module.params.get("location")) else: self.module.fail_json(msg="server or location is required") @@ -227,8 +222,8 @@ class AnsibleHcloudVolume(Hcloud): if delete_protection is not None: self._get_volume() self.hcloud_volume.change_protection(delete=delete_protection).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) self._mark_as_changed() self._get_volume() @@ -270,8 +265,8 @@ class AnsibleHcloudVolume(Hcloud): self._mark_as_changed() self._get_volume() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) def present_volume(self): self._get_volume() @@ -290,11 +285,11 @@ class AnsibleHcloudVolume(Hcloud): self.client.volumes.delete(self.hcloud_volume) self._mark_as_changed() self.hcloud_volume = None - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, @@ -304,31 +299,27 @@ class AnsibleHcloudVolume(Hcloud): server={"type": "str"}, labels={"type": "dict"}, automount={"type": "bool", "default": False}, - format={"type": "str", - "choices": ['xfs', 'ext4'], - }, + format={"type": "str", "choices": ["xfs", "ext4"]}, delete_protection={"type": "bool"}, state={ "choices": ["absent", "present"], "default": "present", }, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), - required_one_of=[['id', 'name']], + required_one_of=[["id", "name"]], mutually_exclusive=[["location", "server"]], supports_check_mode=True, ) def main(): - module = AnsibleHcloudVolume.define_module() + module = AnsibleHCloudVolume.define_module() - hcloud = AnsibleHcloudVolume(module) + hcloud = AnsibleHCloudVolume(module) state = module.params.get("state") if state == "absent": - module.fail_on_missing_params( - required_params=["name"] - ) + module.fail_on_missing_params(required_params=["name"]) hcloud.delete_volume() else: hcloud.present_volume() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_volume_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/volume_info.py index 9520bfa14..1e507690e 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_volume_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/volume_info.py @@ -1,16 +1,14 @@ #!/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 +from __future__ import annotations -DOCUMENTATION = ''' +DOCUMENTATION = """ --- -module: hcloud_volume_info +module: volume_info short_description: Gather infos about your Hetzner Cloud Volumes. @@ -24,6 +22,7 @@ options: id: description: - The ID of the Volume you want to get. + - The module will fail if the provided ID is invalid. type: int name: description: @@ -36,11 +35,11 @@ options: extends_documentation_fragment: - hetzner.hcloud.hcloud -''' +""" EXAMPLES = """ - name: Gather hcloud Volume infos - hcloud_volume_info: + hetzner.hcloud.volume_info: register: output - name: Print the gathered infos debug: @@ -96,14 +95,17 @@ hcloud_volume_info: """ from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.volumes import BoundVolume + +class AnsibleHCloudVolumeInfo(AnsibleHCloud): + represent = "hcloud_volume_info" -class AnsibleHcloudVolumeInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_volume_info") - self.hcloud_volume_info = None + hcloud_volume_info: list[BoundVolume] | None = None def _prepare_result(self): tmp = [] @@ -113,73 +115,59 @@ class AnsibleHcloudVolumeInfo(Hcloud): server_name = None if volume.server is not None: server_name = to_native(volume.server.name) - tmp.append({ - "id": to_native(volume.id), - "name": to_native(volume.name), - "size": volume.size, - "location": to_native(volume.location.name), - "labels": volume.labels, - "server": server_name, - "linux_device": to_native(volume.linux_device), - "delete_protection": volume.protection["delete"], - }) + tmp.append( + { + "id": to_native(volume.id), + "name": to_native(volume.name), + "size": volume.size, + "location": to_native(volume.location.name), + "labels": volume.labels, + "server": server_name, + "linux_device": to_native(volume.linux_device), + "delete_protection": volume.protection["delete"], + } + ) return tmp def get_volumes(self): try: if self.module.params.get("id") is not None: - self.hcloud_volume_info = [self.client.volumes.get_by_id( - self.module.params.get("id") - )] + self.hcloud_volume_info = [self.client.volumes.get_by_id(self.module.params.get("id"))] elif self.module.params.get("name") is not None: - self.hcloud_volume_info = [self.client.volumes.get_by_name( - self.module.params.get("name") - )] + self.hcloud_volume_info = [self.client.volumes.get_by_name(self.module.params.get("name"))] elif self.module.params.get("label_selector") is not None: self.hcloud_volume_info = self.client.volumes.get_all( - label_selector=self.module.params.get("label_selector")) + label_selector=self.module.params.get("label_selector") + ) else: self.hcloud_volume_info = self.client.volumes.get_all() - except Exception as e: - self.module.fail_json(msg=e.message) + except HCloudException as exception: + self.fail_json_hcloud(exception) - @staticmethod - def define_module(): + @classmethod + def define_module(cls): return AnsibleModule( argument_spec=dict( id={"type": "int"}, name={"type": "str"}, label_selector={"type": "str"}, - **Hcloud.base_module_arguments() + **super().base_module_arguments(), ), supports_check_mode=True, ) def main(): - module = AnsibleHcloudVolumeInfo.define_module() - - is_old_facts = module._name == 'hcloud_volume_facts' - if is_old_facts: - module.deprecate("The 'hcloud_volume_facts' module has been renamed to 'hcloud_volume_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudVolumeInfo(module) + module = AnsibleHCloudVolumeInfo.define_module() + hcloud = AnsibleHCloudVolumeInfo(module) hcloud.get_volumes() result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_volume_facts': result['hcloud_volume_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_volume_info': result['hcloud_volume_info'] - } - module.exit_json(**ansible_info) + + ansible_info = {"hcloud_volume_info": result["hcloud_volume_info"]} + module.exit_json(**ansible_info) if __name__ == "__main__": |