diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-05 16:18:34 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-05 16:18:34 +0000 |
commit | 3667197efb7b18ec842efd504785965911f8ac4b (patch) | |
tree | 0b986a4bc6879d080b100666a97cdabbc9ca1f28 /ansible_collections/hetzner/hcloud/plugins | |
parent | Adding upstream version 9.5.1+dfsg. (diff) | |
download | ansible-3667197efb7b18ec842efd504785965911f8ac4b.tar.xz ansible-3667197efb7b18ec842efd504785965911f8ac4b.zip |
Adding upstream version 10.0.0+dfsg.upstream/10.0.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/hetzner/hcloud/plugins')
46 files changed, 1052 insertions, 891 deletions
diff --git a/ansible_collections/hetzner/hcloud/plugins/inventory/hcloud.py b/ansible_collections/hetzner/hcloud/plugins/inventory/hcloud.py index 449342271..d1050c92b 100644 --- a/ansible_collections/hetzner/hcloud/plugins/inventory/hcloud.py +++ b/ansible_collections/hetzner/hcloud/plugins/inventory/hcloud.py @@ -32,21 +32,10 @@ options: description: - The API Token for the Hetzner Cloud. type: str - required: false # TODO: Mark as required once 'api_token_env' is removed. + required: true aliases: [token] env: - name: HCLOUD_TOKEN - api_token_env: - description: - - Environment variable name to load the Hetzner Cloud API Token from. - type: str - default: HCLOUD_TOKEN - aliases: [token_env] - deprecated: - why: The option is adding too much complexity, while the alternatives are preferred. - collection_name: hetzner.hcloud - version: 3.0.0 - alternatives: Use the P(ansible.builtin.env#lookup) lookup plugin instead. api_endpoint: description: - The API Endpoint for the Hetzner Cloud. @@ -120,6 +109,14 @@ options: - The suffix for host variables names coming from Hetzner Cloud. type: str version_added: 2.5.0 + + hostname: + description: + - A template for the instances hostname, if not provided the Hetzner Cloud server name will be used. + - Available variables are the Hetzner Cloud host variables. + - The available variables names are provide with the O(hostvars_prefix) or O(hostvars_suffix) modifications. + type: str + version_added: 3.0.0 """ EXAMPLES = """ @@ -153,9 +150,43 @@ keyed_groups: separator: "" - key: status prefix: server_status + +--- +# Use a custom hostname template. +plugin: hetzner.hcloud.hcloud + +# Available variables are for example: +## Server +# id: 42984895 +# name: "my-server" +# labels: +# foo: "bar" +# status: "running" +## Server Type +# type: "cx11" +# server_type: "cx11" +# architecture: "x86" +## Image +# image_id: 114690387 +# image_name: "debian-12" +# image_os_flavor: "debian" +## Datacenter +# datacenter: "hel1-dc2" +# location: "hel1" +## Network +# ipv4: "65.109.140.95" # Value is optional! +# ipv6: "2a01:4f9:c011:b83f::1" # Value is optional! +# ipv6_network: 2a01:4f9:c011:b83f::" # Value is optional! +# ipv6_network_mask: "64" # Value is optional! +# private_ipv4: "10.0.0.3" # Value is optional! +# private_networks: +# - id: 114690387 +# name: "my-private-network" +# ip: "10.0.0.3" +# +hostname: "my-prefix-{{ datacenter }}-{{ name }}-{{ server_type }}" """ -import os import sys from ipaddress import IPv6Network @@ -164,6 +195,7 @@ from ansible.inventory.manager import InventoryData from ansible.module_utils.common.text.converters import to_native from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable, Constructable from ansible.utils.display import Display +from ansible.utils.vars import combine_vars from ..module_utils.client import ( Client, @@ -229,7 +261,7 @@ def first_ipv6_address(network: str) -> str: :param network: IPv6 Network. """ - return next(IPv6Network(network).hosts()) + return str(next(IPv6Network(network).hosts())) class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): @@ -243,30 +275,9 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): network: Network | None def _configure_hcloud_client(self): - # If api_token_env is not the default, print a deprecation warning and load the - # environment variable. - api_token_env = self.get_option("api_token_env") - if api_token_env != "HCLOUD_TOKEN": - self.display.deprecated( - "The 'api_token_env' option is deprecated, please use the 'HCLOUD_TOKEN' " - "environment variable or use the 'ansible.builtin.env' lookup instead.", - version="3.0.0", - collection_name="hetzner.hcloud", - ) - if api_token_env in os.environ: - self.set_option("api_token", os.environ.get(api_token_env)) - api_token = self.get_option("api_token") api_endpoint = self.get_option("api_endpoint") - if api_token is None: # TODO: Remove once I(api_token_env) is removed. - raise AnsibleError( - "No setting was provided for required configuration setting: " - "plugin_type: inventory " - "plugin: hetzner.hcloud.hcloud " - "setting: api_token" - ) - # Resolve template string api_token = self.templar.template(api_token) @@ -325,43 +336,43 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): def _build_inventory_server(self, server: Server) -> InventoryServer: server_dict: InventoryServer = {} server_dict["id"] = server.id - server_dict["name"] = to_native(server.name) - server_dict["status"] = to_native(server.status) + server_dict["name"] = server.name + server_dict["status"] = server.status # Server Type - server_dict["type"] = to_native(server.server_type.name) - server_dict["server_type"] = to_native(server.server_type.name) - server_dict["architecture"] = to_native(server.server_type.architecture) + server_dict["type"] = server.server_type.name + server_dict["server_type"] = server.server_type.name + server_dict["architecture"] = server.server_type.architecture # Network if server.public_net.ipv4: - server_dict["ipv4"] = to_native(server.public_net.ipv4.ip) + server_dict["ipv4"] = server.public_net.ipv4.ip if server.public_net.ipv6: - server_dict["ipv6"] = to_native(first_ipv6_address(server.public_net.ipv6.ip)) - server_dict["ipv6_network"] = to_native(server.public_net.ipv6.network) - server_dict["ipv6_network_mask"] = to_native(server.public_net.ipv6.network_mask) + server_dict["ipv6"] = first_ipv6_address(server.public_net.ipv6.ip) + server_dict["ipv6_network"] = server.public_net.ipv6.network + server_dict["ipv6_network_mask"] = server.public_net.ipv6.network_mask server_dict["private_networks"] = [ - {"id": v.network.id, "name": to_native(v.network.name), "ip": to_native(v.ip)} for v in server.private_net + {"id": v.network.id, "name": v.network.name, "ip": v.ip} for v in server.private_net ] if self.get_option("network"): for private_net in server.private_net: # Set private_ipv4 if user filtered for one network if private_net.network.id == self.network.id: - server_dict["private_ipv4"] = to_native(private_net.ip) + server_dict["private_ipv4"] = private_net.ip break # Datacenter - server_dict["datacenter"] = to_native(server.datacenter.name) - server_dict["location"] = to_native(server.datacenter.location.name) + server_dict["datacenter"] = server.datacenter.name + server_dict["location"] = server.datacenter.location.name # Image if server.image is not None: server_dict["image_id"] = server.image.id - server_dict["image_os_flavor"] = to_native(server.image.os_flavor) - server_dict["image_name"] = to_native(server.image.name or server.image.description) + server_dict["image_os_flavor"] = server.image.os_flavor + server_dict["image_name"] = server.image.name or server.image.description # Labels server_dict["labels"] = dict(server.labels) @@ -380,28 +391,28 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): def _get_server_ansible_host(self, server: Server): if self.get_option("connect_with") == "public_ipv4": if server.public_net.ipv4: - return to_native(server.public_net.ipv4.ip) + return server.public_net.ipv4.ip raise AnsibleError("Server has no public ipv4, but connect_with=public_ipv4 was specified") if self.get_option("connect_with") == "public_ipv6": if server.public_net.ipv6: - return to_native(first_ipv6_address(server.public_net.ipv6.ip)) + return first_ipv6_address(server.public_net.ipv6.ip) raise AnsibleError("Server has no public ipv6, but connect_with=public_ipv6 was specified") if self.get_option("connect_with") == "hostname": # every server has a name, no need to guard this - return to_native(server.name) + return server.name if self.get_option("connect_with") == "ipv4_dns_ptr": if server.public_net.ipv4: - return to_native(server.public_net.ipv4.dns_ptr) + return server.public_net.ipv4.dns_ptr raise AnsibleError("Server has no public ipv4, but connect_with=ipv4_dns_ptr was specified") if self.get_option("connect_with") == "private_ipv4": if self.get_option("network"): for private_net in server.private_net: if private_net.network.id == self.network.id: - return to_native(private_net.ip) + return private_net.ip else: raise AnsibleError("You can only connect via private IPv4 if you specify a network") @@ -466,9 +477,10 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): hostvars_prefix = self.get_option("hostvars_prefix") hostvars_suffix = self.get_option("hostvars_suffix") + hostname_template = self.get_option("hostname") for server in servers: - self.inventory.add_host(server["name"], group=self.get_option("group")) + hostvars = {} for key, value in server.items(): # Add hostvars prefix and suffix for variables coming from the Hetzner Cloud. if hostvars_prefix or hostvars_suffix: @@ -478,7 +490,18 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): if hostvars_suffix: key = key + hostvars_suffix - self.inventory.set_variable(server["name"], key, value) + hostvars[key] = value + + if hostname_template: + templar = self.templar + templar.available_variables = combine_vars(hostvars, self._vars) + hostname = templar.template(hostname_template) + else: + hostname = server["name"] + + self.inventory.add_host(hostname, group=self.get_option("group")) + for key, value in hostvars.items(): + self.inventory.set_variable(hostname, key, value) # Use constructed if applicable strict = self.get_option("strict") @@ -486,8 +509,8 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): # Composed variables self._set_composite_vars( self.get_option("compose"), - self.inventory.get_host(server["name"]).get_vars(), - server["name"], + self.inventory.get_host(hostname).get_vars(), + hostname, strict=strict, ) @@ -495,7 +518,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): self._add_host_to_composed_groups( self.get_option("groups"), {}, - server["name"], + hostname, strict=strict, ) @@ -503,7 +526,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): self._add_host_to_keyed_groups( self.get_option("keyed_groups"), {}, - server["name"], + hostname, strict=strict, ) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/hcloud.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/hcloud.py index eab0aef59..60391308a 100644 --- a/ansible_collections/hetzner/hcloud/plugins/module_utils/hcloud.py +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/hcloud.py @@ -10,6 +10,10 @@ from typing import Any, NoReturn from ansible.module_utils.basic import AnsibleModule as AnsibleModuleBase, env_fallback from ansible.module_utils.common.text.converters import to_native +from ansible.module_utils.common.validation import ( + check_missing_parameters, + check_required_one_of, +) from .client import ClientException, client_check_required_lib, client_get_by_name_or_id from .vendor.hcloud import APIException, Client, HCloudException @@ -94,6 +98,29 @@ class AnsibleHCloud: def _mark_as_changed(self) -> None: self.result["changed"] = True + def fail_on_invalid_params( + self, + *, + required: list[str] | None = None, + required_one_of: list[list[str]] | None = None, + ) -> None: + """ + Run additional validation that cannot be done in the argument spec validation. + + :param required_params: Check that terms exists in the module params. + :param required_one_of: Check each list of terms to ensure at least one exists in the module parameters. + """ + try: + if required: + check_missing_parameters(self.module.params, required) + + if required_one_of: + params_without_nones = {k: v for k, v in self.module.params.items() if v is not None} + check_required_one_of(required_one_of, params_without_nones) + + except TypeError as e: + self.module.fail_json(msg=to_native(e)) + @classmethod def base_module_arguments(cls): return { diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/_version.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/_version.py index e03c1b434..d350d7138 100644 --- a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/_version.py +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/_version.py @@ -1,3 +1,3 @@ from __future__ import annotations -VERSION = "1.33.2" # x-release-please-version +VERSION = "1.35.0" # x-release-please-version diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/core/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/core/domain.py index 692f7488b..bba954fe2 100644 --- a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/core/domain.py +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/core/domain.py @@ -34,6 +34,24 @@ class DomainIdentityMixin: return self.name raise ValueError("id or name must be set") + def has_id_or_name(self, id_or_name: int | str) -> bool: + """ + Return whether this domain has the same id or same name as the other. + + The domain calling this method MUST be a bound domain or be populated, otherwise + the comparison will not work as expected (e.g. the domains are the same but + cannot be equal, if one provides an id and the other the name). + """ + values: list[int | str] = [] + if self.id is not None: + values.append(self.id) + if self.name is not None: + values.append(self.name) + if not values: + raise ValueError("id or name must be set") + + return id_or_name in values + class Pagination(BaseDomain): __slots__ = ( diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/firewalls/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/firewalls/domain.py index 5ce9281d9..d637231d6 100644 --- a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/firewalls/domain.py +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/firewalls/domain.py @@ -7,7 +7,7 @@ try: except ImportError: isoparse = None -from ..core import BaseDomain +from ..core import BaseDomain, DomainIdentityMixin if TYPE_CHECKING: from ..actions import BoundAction @@ -15,7 +15,7 @@ if TYPE_CHECKING: from .client import BoundFirewall -class Firewall(BaseDomain): +class Firewall(BaseDomain, DomainIdentityMixin): """Firewall Domain :param id: int diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/floating_ips/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/floating_ips/domain.py index e1f295bd6..abd2c13cc 100644 --- a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/floating_ips/domain.py +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/floating_ips/domain.py @@ -7,7 +7,7 @@ try: except ImportError: isoparse = None -from ..core import BaseDomain +from ..core import BaseDomain, DomainIdentityMixin if TYPE_CHECKING: from ..actions import BoundAction @@ -16,7 +16,7 @@ if TYPE_CHECKING: from .client import BoundFloatingIP -class FloatingIP(BaseDomain): +class FloatingIP(BaseDomain, DomainIdentityMixin): """Floating IP Domain :param id: int diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/images/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/images/client.py index 65b7546a8..a7d41e01b 100644 --- a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/images/client.py +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/images/client.py @@ -1,5 +1,6 @@ from __future__ import annotations +import warnings from typing import TYPE_CHECKING, Any, NamedTuple from ..actions import ActionsPageResult, BoundAction, ResourceActionsClient @@ -305,18 +306,27 @@ class ImagesClient(ClientEntityBase): def get_by_name(self, name: str) -> BoundImage | None: """Get image by name - Deprecated: Use get_by_name_and_architecture instead. - :param name: str Used to get image by name. :return: :class:`BoundImage <hcloud.images.client.BoundImage>` + + .. deprecated:: 1.19 + Use :func:`hcloud.images.client.ImagesClient.get_by_name_and_architecture` instead. """ + warnings.warn( + "The 'hcloud.images.client.ImagesClient.get_by_name' method is deprecated, please use the " + "'hcloud.images.client.ImagesClient.get_by_name_and_architecture' method instead.", + DeprecationWarning, + stacklevel=2, + ) return self._get_first_by(name=name) def get_by_name_and_architecture( self, name: str, architecture: str, + *, + include_deprecated: bool | None = None, ) -> BoundImage | None: """Get image by name @@ -324,9 +334,15 @@ class ImagesClient(ClientEntityBase): Used to identify the image. :param architecture: str Used to identify the image. + :param include_deprecated: bool (optional) + Include deprecated images. Default: False :return: :class:`BoundImage <hcloud.images.client.BoundImage>` """ - return self._get_first_by(name=name, architecture=[architecture]) + return self._get_first_by( + name=name, + architecture=[architecture], + include_deprecated=include_deprecated, + ) def update( self, diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancers/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancers/domain.py index 6d6f0700a..76e8db3f7 100644 --- a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancers/domain.py +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancers/domain.py @@ -7,7 +7,7 @@ try: except ImportError: isoparse = None -from ..core import BaseDomain +from ..core import BaseDomain, DomainIdentityMixin if TYPE_CHECKING: from ..actions import BoundAction @@ -20,7 +20,7 @@ if TYPE_CHECKING: from .client import BoundLoadBalancer -class LoadBalancer(BaseDomain): +class LoadBalancer(BaseDomain, DomainIdentityMixin): """LoadBalancer Domain :param id: int @@ -79,7 +79,7 @@ class LoadBalancer(BaseDomain): id: int, name: str | None = None, public_net: PublicNetwork | None = None, - private_net: PrivateNet | None = None, + private_net: list[PrivateNet] | None = None, location: BoundLocation | None = None, algorithm: LoadBalancerAlgorithm | None = None, services: list[LoadBalancerService] | None = None, @@ -203,9 +203,9 @@ class LoadBalancerService(BaseDomain): if self.health_check.http.response is not None: health_check_http["response"] = self.health_check.http.response if self.health_check.http.status_codes is not None: - health_check_http[ - "status_codes" - ] = self.health_check.http.status_codes + health_check_http["status_codes"] = ( + self.health_check.http.status_codes + ) if self.health_check.http.tls is not None: health_check_http["tls"] = self.health_check.http.tls diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/networks/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/networks/domain.py index c307bf989..e04de2775 100644 --- a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/networks/domain.py +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/networks/domain.py @@ -7,7 +7,7 @@ try: except ImportError: isoparse = None -from ..core import BaseDomain +from ..core import BaseDomain, DomainIdentityMixin if TYPE_CHECKING: from ..actions import BoundAction @@ -15,7 +15,7 @@ if TYPE_CHECKING: from .client import BoundNetwork -class Network(BaseDomain): +class Network(BaseDomain, DomainIdentityMixin): """Network Domain :param id: int diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/placement_groups/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/placement_groups/domain.py index 16b2a390d..1c6fc04cd 100644 --- a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/placement_groups/domain.py +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/placement_groups/domain.py @@ -7,14 +7,14 @@ try: except ImportError: isoparse = None -from ..core import BaseDomain +from ..core import BaseDomain, DomainIdentityMixin if TYPE_CHECKING: from ..actions import BoundAction from .client import BoundPlacementGroup -class PlacementGroup(BaseDomain): +class PlacementGroup(BaseDomain, DomainIdentityMixin): """Placement Group Domain :param id: int diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/primary_ips/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/primary_ips/domain.py index aeb943f0a..2eebacef4 100644 --- a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/primary_ips/domain.py +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/primary_ips/domain.py @@ -7,7 +7,7 @@ try: except ImportError: isoparse = None -from ..core import BaseDomain +from ..core import BaseDomain, DomainIdentityMixin if TYPE_CHECKING: from ..actions import BoundAction @@ -15,7 +15,7 @@ if TYPE_CHECKING: from .client import BoundPrimaryIP -class PrimaryIP(BaseDomain): +class PrimaryIP(BaseDomain, DomainIdentityMixin): """Primary IP Domain :param id: int diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/servers/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/servers/domain.py index 0a0d34688..d5e769e5e 100644 --- a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/servers/domain.py +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/servers/domain.py @@ -7,7 +7,7 @@ try: except ImportError: isoparse = None -from ..core import BaseDomain +from ..core import BaseDomain, DomainIdentityMixin if TYPE_CHECKING: from ..actions import BoundAction @@ -25,7 +25,7 @@ if TYPE_CHECKING: from .client import BoundServer -class Server(BaseDomain): +class Server(BaseDomain, DomainIdentityMixin): """Server Domain :param id: int diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/version.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/version.py index e78c320a2..0bf903564 100644 --- a/ansible_collections/hetzner/hcloud/plugins/module_utils/version.py +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/version.py @@ -1,3 +1,3 @@ from __future__ import annotations -version = "2.5.0" # x-release-please-version +version = "3.1.1" # x-release-please-version diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/certificate.py b/ansible_collections/hetzner/hcloud/plugins/modules/certificate.py index ea39be6ca..53466e48f 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/certificate.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/certificate.py @@ -146,7 +146,6 @@ hcloud_certificate: """ 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 @@ -160,14 +159,14 @@ class AnsibleHCloudCertificate(AnsibleHCloud): def _prepare_result(self): return { - "id": to_native(self.hcloud_certificate.id), - "name": to_native(self.hcloud_certificate.name), - "type": to_native(self.hcloud_certificate.type), - "fingerprint": to_native(self.hcloud_certificate.fingerprint), - "certificate": to_native(self.hcloud_certificate.certificate), - "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], + "id": str(self.hcloud_certificate.id), + "name": self.hcloud_certificate.name, + "type": self.hcloud_certificate.type, + "fingerprint": self.hcloud_certificate.fingerprint, + "certificate": self.hcloud_certificate.certificate, + "not_valid_before": self.hcloud_certificate.not_valid_before.isoformat(), + "not_valid_after": self.hcloud_certificate.not_valid_after.isoformat(), + "domain_names": self.hcloud_certificate.domain_names, "labels": self.hcloud_certificate.labels, } @@ -203,7 +202,9 @@ class AnsibleHCloudCertificate(AnsibleHCloud): if not self.module.check_mode: try: resp = self.client.certificates.create_managed(**params) - resp.action.wait_until_finished(max_retries=1000) + # Action should take 60 to 90 seconds on average, wait for 5m to + # allow DNS or Let's Encrypt slowdowns. + resp.action.wait_until_finished(max_retries=300) except HCloudException as exception: self.fail_json_hcloud(exception) diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/certificate_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/certificate_info.py index e074046fd..37b944341 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/certificate_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/certificate_info.py @@ -87,7 +87,6 @@ hcloud_certificate_info: """ 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 @@ -100,23 +99,25 @@ class AnsibleHCloudCertificateInfo(AnsibleHCloud): hcloud_certificate_info: list[BoundCertificate] | None = None def _prepare_result(self): - certificates = [] + tmp = [] 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, - } - ) - return certificates + if certificate is None: + continue + + tmp.append( + { + "id": str(certificate.id), + "name": certificate.name, + "fingerprint": certificate.fingerprint, + "certificate": certificate.certificate, + "not_valid_before": certificate.not_valid_before.isoformat(), + "not_valid_after": certificate.not_valid_after.isoformat(), + "domain_names": certificate.domain_names, + "labels": certificate.labels, + } + ) + return tmp def get_certificates(self): try: diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/datacenter_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/datacenter_info.py index f6665a6fb..85475dd44 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/datacenter_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/datacenter_info.py @@ -118,7 +118,6 @@ hcloud_datacenter_info: """ 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 @@ -139,10 +138,10 @@ class AnsibleHCloudDatacenterInfo(AnsibleHCloud): tmp.append( { - "id": to_native(datacenter.id), - "name": to_native(datacenter.name), - "description": to_native(datacenter.description), - "location": to_native(datacenter.location.name), + "id": str(datacenter.id), + "name": datacenter.name, + "description": datacenter.description, + "location": 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], diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/firewall.py b/ansible_collections/hetzner/hcloud/plugins/modules/firewall.py index 3c51b5c0a..30f17eb9b 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/firewall.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/firewall.py @@ -219,7 +219,6 @@ hcloud_firewall: import time 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 @@ -237,8 +236,8 @@ class AnsibleHCloudFirewall(AnsibleHCloud): def _prepare_result(self): return { - "id": to_native(self.hcloud_firewall.id), - "name": to_native(self.hcloud_firewall.name), + "id": str(self.hcloud_firewall.id), + "name": self.hcloud_firewall.name, "rules": [self._prepare_result_rule(rule) for rule in self.hcloud_firewall.rules], "labels": self.hcloud_firewall.labels, "applied_to": [self._prepare_result_applied_to(resource) for resource in self.hcloud_firewall.applied_to], @@ -246,27 +245,25 @@ class AnsibleHCloudFirewall(AnsibleHCloud): def _prepare_result_rule(self, rule: FirewallRule): return { - "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], - "description": to_native(rule.description) if rule.description is not None else None, + "direction": rule.direction, + "protocol": rule.protocol, + "port": rule.port, + "source_ips": rule.source_ips, + "destination_ips": rule.destination_ips, + "description": rule.description, } 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 - ), + "type": resource.type, + "server": str(resource.server.id) if resource.server is not None else None, + "label_selector": 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, + "type": item.type, + "server": str(item.server.id) if item.server is not None else None, } for item in resource.applied_to_resources ] diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/firewall_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/firewall_info.py index 7e7a623d0..a07d9ec54 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/firewall_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/firewall_info.py @@ -142,7 +142,6 @@ hcloud_firewall_info: """ 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 @@ -167,8 +166,8 @@ class AnsibleHCloudFirewallInfo(AnsibleHCloud): tmp.append( { - "id": to_native(firewall.id), - "name": to_native(firewall.name), + "id": str(firewall.id), + "name": 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], @@ -179,27 +178,25 @@ class AnsibleHCloudFirewallInfo(AnsibleHCloud): 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], + "description": rule.description, + "direction": rule.direction, + "protocol": rule.protocol, + "port": rule.port, + "source_ips": rule.source_ips, + "destination_ips": 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 - ), + "type": resource.type, + "server": str(resource.server.id) if resource.server is not None else None, + "label_selector": 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, + "type": item.type, + "server": str(item.server.id) if item.server is not None else None, } for item in resource.applied_to_resources ] diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/firewall_resource.py b/ansible_collections/hetzner/hcloud/plugins/modules/firewall_resource.py index 207f27092..7c0271ba3 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/firewall_resource.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/firewall_resource.py @@ -103,7 +103,6 @@ hcloud_firewall_resource: """ 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 @@ -125,12 +124,12 @@ class AnsibleHCloudFirewallResource(AnsibleHCloud): label_selectors = [] for resource in self.hcloud_firewall_resource.applied_to: if resource.type == FirewallResource.TYPE_SERVER: - servers.append(to_native(resource.server.name)) + servers.append(resource.server.name) elif resource.type == FirewallResource.TYPE_LABEL_SELECTOR: - label_selectors.append(to_native(resource.label_selector.selector)) + label_selectors.append(resource.label_selector.selector) return { - "firewall": to_native(self.hcloud_firewall_resource.name), + "firewall": self.hcloud_firewall_resource.name, "servers": servers, "label_selectors": label_selectors, } diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/floating_ip.py b/ansible_collections/hetzner/hcloud/plugins/modules/floating_ip.py index e037dd7a1..022036124 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/floating_ip.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/floating_ip.py @@ -160,7 +160,6 @@ hcloud_floating_ip: """ 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 @@ -173,19 +172,15 @@ class AnsibleHCloudFloatingIP(AnsibleHCloud): hcloud_floating_ip: BoundFloatingIP | None = None def _prepare_result(self): - server = None - - if self.hcloud_floating_ip.server is not None: - server = to_native(self.hcloud_floating_ip.server.name) return { - "id": to_native(self.hcloud_floating_ip.id), - "name": to_native(self.hcloud_floating_ip.name), - "description": to_native(self.hcloud_floating_ip.description), - "ip": to_native(self.hcloud_floating_ip.ip), - "type": to_native(self.hcloud_floating_ip.type), - "home_location": to_native(self.hcloud_floating_ip.home_location.name), + "id": str(self.hcloud_floating_ip.id), + "name": self.hcloud_floating_ip.name, + "description": self.hcloud_floating_ip.description, + "ip": self.hcloud_floating_ip.ip, + "type": self.hcloud_floating_ip.type, + "home_location": self.hcloud_floating_ip.home_location.name, "labels": self.hcloud_floating_ip.labels, - "server": server, + "server": self.hcloud_floating_ip.server.name if self.hcloud_floating_ip.server is not None else None, "delete_protection": self.hcloud_floating_ip.protection["delete"], } @@ -221,7 +216,8 @@ class AnsibleHCloudFloatingIP(AnsibleHCloud): 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() + action = self.hcloud_floating_ip.change_protection(delete=delete_protection) + action.wait_until_finished() except HCloudException as exception: self.fail_json_hcloud(exception) self._mark_as_changed() @@ -266,7 +262,8 @@ class AnsibleHCloudFloatingIP(AnsibleHCloud): delete_protection = self.module.params.get("delete_protection") if delete_protection is not None and delete_protection != self.hcloud_floating_ip.protection["delete"]: if not self.module.check_mode: - self.hcloud_floating_ip.change_protection(delete=delete_protection).wait_until_finished() + action = self.hcloud_floating_ip.change_protection(delete=delete_protection) + action.wait_until_finished() self._mark_as_changed() self._get_floating_ip() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/floating_ip_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/floating_ip_info.py index 663d29622..6595799ba 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/floating_ip_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/floating_ip_info.py @@ -100,7 +100,6 @@ hcloud_floating_ip_info: """ 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 @@ -116,23 +115,22 @@ class AnsibleHCloudFloatingIPInfo(AnsibleHCloud): 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"], - } - ) + if floating_ip is None: + continue + + tmp.append( + { + "id": str(floating_ip.id), + "name": floating_ip.name, + "description": floating_ip.description, + "ip": floating_ip.ip, + "type": floating_ip.type, + "server": floating_ip.server.name if floating_ip.server is not None else None, + "home_location": floating_ip.home_location.name, + "labels": floating_ip.labels, + "delete_protection": floating_ip.protection["delete"], + } + ) return tmp diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/image_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/image_info.py index b0d7fc482..a3a1331c5 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/image_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/image_info.py @@ -112,7 +112,6 @@ hcloud_image_info: """ 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 @@ -128,20 +127,22 @@ class AnsibleHCloudImageInfo(AnsibleHCloud): 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, - } - ) + if image is None: + continue + + tmp.append( + { + "id": str(image.id), + "status": image.status, + "type": image.type, + "name": image.name, + "description": image.description, + "os_flavor": image.os_flavor, + "os_version": image.os_version, + "architecture": image.architecture, + "labels": image.labels, + } + ) return tmp def get_images(self): diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/iso_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/iso_info.py index e623d1714..aab33cab2 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/iso_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/iso_info.py @@ -122,7 +122,6 @@ hcloud_iso_info: """ 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 @@ -143,13 +142,13 @@ class AnsibleHCloudIsoInfo(AnsibleHCloud): tmp.append( { - "id": to_native(iso_info.id), - "name": to_native(iso_info.name), - "description": to_native(iso_info.description), + "id": str(iso_info.id), + "name": iso_info.name, + "description": 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 + iso_info.deprecation.unavailable_after.isoformat() if iso_info.deprecation is not None else None ), "deprecation": ( { diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer.py b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer.py index 1a0d8712a..11dbf96fb 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer.py @@ -152,7 +152,6 @@ hcloud_load_balancer: """ 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 @@ -168,23 +167,20 @@ class AnsibleHCloudLoadBalancer(AnsibleHCloud): 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) - ) return { - "id": to_native(self.hcloud_load_balancer.id), - "name": to_native(self.hcloud_load_balancer.name), - "ipv4_address": to_native(self.hcloud_load_balancer.public_net.ipv4.ip), - "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), + "id": str(self.hcloud_load_balancer.id), + "name": self.hcloud_load_balancer.name, + "ipv4_address": self.hcloud_load_balancer.public_net.ipv4.ip, + "ipv6_address": self.hcloud_load_balancer.public_net.ipv6.ip, + "private_ipv4_address": ( + self.hcloud_load_balancer.private_net[0].ip if len(self.hcloud_load_balancer.private_net) else None + ), + "load_balancer_type": self.hcloud_load_balancer.load_balancer_type.name, + "algorithm": self.hcloud_load_balancer.algorithm.type, + "location": self.hcloud_load_balancer.location.name, "labels": self.hcloud_load_balancer.labels, "delete_protection": self.hcloud_load_balancer.protection["delete"], - "disable_public_interface": False if self.hcloud_load_balancer.public_net.enabled else True, + "disable_public_interface": not self.hcloud_load_balancer.public_net.enabled, } def _get_load_balancer(self): @@ -217,12 +213,13 @@ class AnsibleHCloudLoadBalancer(AnsibleHCloud): if not self.module.check_mode: resp = self.client.load_balancers.create(**params) - resp.action.wait_until_finished(max_retries=1000) + resp.action.wait_until_finished() delete_protection = self.module.params.get("delete_protection") if delete_protection is not None: self._get_load_balancer() - self.hcloud_load_balancer.change_protection(delete=delete_protection).wait_until_finished() + action = self.hcloud_load_balancer.change_protection(delete=delete_protection) + action.wait_until_finished() except HCloudException as exception: self.fail_json_hcloud(exception) self._mark_as_changed() @@ -239,7 +236,8 @@ class AnsibleHCloudLoadBalancer(AnsibleHCloud): delete_protection = self.module.params.get("delete_protection") if delete_protection is not None and delete_protection != self.hcloud_load_balancer.protection["delete"]: if not self.module.check_mode: - self.hcloud_load_balancer.change_protection(delete=delete_protection).wait_until_finished() + action = self.hcloud_load_balancer.change_protection(delete=delete_protection) + action.wait_until_finished() self._mark_as_changed() self._get_load_balancer() @@ -249,9 +247,11 @@ class AnsibleHCloudLoadBalancer(AnsibleHCloud): ): if not self.module.check_mode: if disable_public_interface is True: - self.hcloud_load_balancer.disable_public_interface().wait_until_finished() + action = self.hcloud_load_balancer.disable_public_interface() + action.wait_until_finished() else: - self.hcloud_load_balancer.enable_public_interface().wait_until_finished() + action = self.hcloud_load_balancer.enable_public_interface() + action.wait_until_finished() self._mark_as_changed() load_balancer_type = self.module.params.get("load_balancer_type") @@ -263,17 +263,17 @@ class AnsibleHCloudLoadBalancer(AnsibleHCloud): if not new_load_balancer_type: self.module.fail_json(msg="unknown load balancer type") if not self.module.check_mode: - self.hcloud_load_balancer.change_type( + action = self.hcloud_load_balancer.change_type( load_balancer_type=new_load_balancer_type, - ).wait_until_finished(max_retries=1000) + ) + action.wait_until_finished() 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() + action = self.hcloud_load_balancer.change_algorithm(algorithm=LoadBalancerAlgorithm(type=algorithm)) + action.wait_until_finished() self._mark_as_changed() self._get_load_balancer() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_info.py index 19ead98c2..4ed5f134b 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_info.py @@ -277,11 +277,14 @@ hcloud_load_balancer_info: """ 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.load_balancers import BoundLoadBalancer +from ..module_utils.vendor.hcloud.load_balancers import ( + BoundLoadBalancer, + LoadBalancerService, + LoadBalancerTarget, +) class AnsibleHCloudLoadBalancerInfo(AnsibleHCloud): @@ -293,44 +296,40 @@ class AnsibleHCloudLoadBalancerInfo(AnsibleHCloud): tmp = [] for load_balancer in self.hcloud_load_balancer_info: - if load_balancer is not None: - 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, - } - ) + if load_balancer is None: + continue + + tmp.append( + { + "id": str(load_balancer.id), + "name": load_balancer.name, + "ipv4_address": load_balancer.public_net.ipv4.ip, + "ipv6_address": load_balancer.public_net.ipv6.ip, + "private_ipv4_address": load_balancer.private_net[0].ip if len(load_balancer.private_net) else None, + "load_balancer_type": load_balancer.load_balancer_type.name, + "location": 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": [self._prepare_target_result(target) for target in load_balancer.targets], + "services": [self._prepare_service_result(service) for service in load_balancer.services], + } + ) return tmp @staticmethod - def _prepare_service_result(service): + def _prepare_service_result(service: LoadBalancerService): http = None if service.protocol != "tcp": http = { - "cookie_name": to_native(service.http.cookie_name), - "cookie_lifetime": service.http.cookie_name, + "cookie_name": service.http.cookie_name, + "cookie_lifetime": service.http.cookie_lifetime, "redirect_http": service.http.redirect_http, "sticky_sessions": service.http.sticky_sessions, - "certificates": [to_native(certificate.name) for certificate in service.http.certificates], + "certificates": [certificate.name for certificate in service.http.certificates], } health_check = { - "protocol": to_native(service.health_check.protocol), + "protocol": service.health_check.protocol, "port": service.health_check.port, "interval": service.health_check.interval, "timeout": service.health_check.timeout, @@ -338,14 +337,14 @@ class AnsibleHCloudLoadBalancerInfo(AnsibleHCloud): } if service.health_check.protocol != "tcp": health_check["http"] = { - "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], + "domain": service.health_check.http.domain, + "path": service.health_check.http.path, + "response": service.health_check.http.response, + "certificates": service.health_check.http.status_codes, "tls": service.health_check.http.tls, } return { - "protocol": to_native(service.protocol), + "protocol": service.protocol, "listen_port": service.listen_port, "destination_port": service.destination_port, "proxyprotocol": service.proxyprotocol, @@ -354,17 +353,17 @@ class AnsibleHCloudLoadBalancerInfo(AnsibleHCloud): } @staticmethod - def _prepare_target_result(target): + def _prepare_target_result(target: LoadBalancerTarget): result = { - "type": to_native(target.type), + "type": target.type, "use_private_ip": target.use_private_ip, } if target.type == "server": - result["server"] = to_native(target.server.name) + result["server"] = target.server.name elif target.type == "label_selector": - result["label_selector"] = to_native(target.label_selector.selector) + result["label_selector"] = target.label_selector.selector elif target.type == "ip": - result["ip"] = to_native(target.ip.ip) + result["ip"] = target.ip.ip if target.health_status is not None: result["health_status"] = [ diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_network.py b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_network.py index 4560f8735..b6ec486ed 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_network.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_network.py @@ -90,7 +90,6 @@ hcloud_load_balancer_network: """ 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 @@ -107,9 +106,9 @@ class AnsibleHCloudLoadBalancerNetwork(AnsibleHCloud): def _prepare_result(self): return { - "network": to_native(self.hcloud_network.name), - "load_balancer": to_native(self.hcloud_load_balancer.name), - "ip": to_native(self.hcloud_load_balancer_network.ip), + "network": self.hcloud_network.name, + "load_balancer": self.hcloud_load_balancer.name, + "ip": self.hcloud_load_balancer_network.ip, } def _get_load_balancer_and_network(self): @@ -140,7 +139,8 @@ class AnsibleHCloudLoadBalancerNetwork(AnsibleHCloud): if not self.module.check_mode: try: - self.hcloud_load_balancer.attach_to_network(**params).wait_until_finished() + action = self.hcloud_load_balancer.attach_to_network(**params) + action.wait_until_finished() except HCloudException as exception: self.fail_json_hcloud(exception) @@ -160,9 +160,8 @@ class AnsibleHCloudLoadBalancerNetwork(AnsibleHCloud): if self.hcloud_load_balancer_network is not None and self.hcloud_load_balancer is not None: if not self.module.check_mode: try: - self.hcloud_load_balancer.detach_from_network( - self.hcloud_load_balancer_network.network - ).wait_until_finished() + action = self.hcloud_load_balancer.detach_from_network(self.hcloud_load_balancer_network.network) + action.wait_until_finished() self._mark_as_changed() except HCloudException as exception: self.fail_json_hcloud(exception) diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_service.py b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_service.py index 1fc18deef..29e37083c 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_service.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_service.py @@ -279,7 +279,6 @@ hcloud_load_balancer_service: """ 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 @@ -302,16 +301,16 @@ class AnsibleHCloudLoadBalancerService(AnsibleHCloud): http = None if self.hcloud_load_balancer_service.protocol != "tcp": http = { - "cookie_name": to_native(self.hcloud_load_balancer_service.http.cookie_name), - "cookie_lifetime": self.hcloud_load_balancer_service.http.cookie_name, + "cookie_name": self.hcloud_load_balancer_service.http.cookie_name, + "cookie_lifetime": self.hcloud_load_balancer_service.http.cookie_lifetime, "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 + 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), + "protocol": self.hcloud_load_balancer_service.health_check.protocol, "port": self.hcloud_load_balancer_service.health_check.port, "interval": self.hcloud_load_balancer_service.health_check.interval, "timeout": self.hcloud_load_balancer_service.health_check.timeout, @@ -319,18 +318,15 @@ class AnsibleHCloudLoadBalancerService(AnsibleHCloud): } if self.hcloud_load_balancer_service.health_check.protocol != "tcp": health_check["http"] = { - "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), - "status_codes": [ - to_native(status_code) - for status_code in self.hcloud_load_balancer_service.health_check.http.status_codes - ], + "domain": self.hcloud_load_balancer_service.health_check.http.domain, + "path": self.hcloud_load_balancer_service.health_check.http.path, + "response": self.hcloud_load_balancer_service.health_check.http.response, + "status_codes": self.hcloud_load_balancer_service.health_check.http.status_codes, "tls": self.hcloud_load_balancer_service.health_check.http.tls, } return { - "load_balancer": to_native(self.hcloud_load_balancer.name), - "protocol": to_native(self.hcloud_load_balancer_service.protocol), + "load_balancer": self.hcloud_load_balancer.name, + "protocol": self.hcloud_load_balancer_service.protocol, "listen_port": self.hcloud_load_balancer_service.listen_port, "destination_port": self.hcloud_load_balancer_service.destination_port, "proxyprotocol": self.hcloud_load_balancer_service.proxyprotocol, @@ -372,9 +368,8 @@ class AnsibleHCloudLoadBalancerService(AnsibleHCloud): if not self.module.check_mode: try: - self.hcloud_load_balancer.add_service(LoadBalancerService(**params)).wait_until_finished( - max_retries=1000 - ) + action = self.hcloud_load_balancer.add_service(LoadBalancerService(**params)) + action.wait_until_finished() except HCloudException as exception: self.fail_json_hcloud(exception) self._mark_as_changed() @@ -468,9 +463,8 @@ class AnsibleHCloudLoadBalancerService(AnsibleHCloud): changed = True if not self.module.check_mode: - self.hcloud_load_balancer.update_service(LoadBalancerService(**params)).wait_until_finished( - max_retries=1000 - ) + action = self.hcloud_load_balancer.update_service(LoadBalancerService(**params)) + action.wait_until_finished() except HCloudException as exception: self.fail_json_hcloud(exception) self._get_load_balancer() @@ -496,9 +490,8 @@ class AnsibleHCloudLoadBalancerService(AnsibleHCloud): if self.hcloud_load_balancer_service is not None: if not self.module.check_mode: try: - self.hcloud_load_balancer.delete_service(self.hcloud_load_balancer_service).wait_until_finished( - max_retries=1000 - ) + action = self.hcloud_load_balancer.delete_service(self.hcloud_load_balancer_service) + action.wait_until_finished() except HCloudException as exception: self.fail_json_hcloud(exception) self._mark_as_changed() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_target.py b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_target.py index 36e7f608f..c392ae712 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_target.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_target.py @@ -134,7 +134,6 @@ hcloud_load_balancer_target: """ 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 @@ -156,17 +155,17 @@ class AnsibleHCloudLoadBalancerTarget(AnsibleHCloud): def _prepare_result(self): result = { - "type": to_native(self.hcloud_load_balancer_target.type), - "load_balancer": to_native(self.hcloud_load_balancer.name), + "type": self.hcloud_load_balancer_target.type, + "load_balancer": self.hcloud_load_balancer.name, "use_private_ip": self.hcloud_load_balancer_target.use_private_ip, } if self.hcloud_load_balancer_target.type == "server": - result["server"] = to_native(self.hcloud_load_balancer_target.server.name) + result["server"] = self.hcloud_load_balancer_target.server.name elif self.hcloud_load_balancer_target.type == "label_selector": - result["label_selector"] = to_native(self.hcloud_load_balancer_target.label_selector.selector) + result["label_selector"] = self.hcloud_load_balancer_target.label_selector.selector elif self.hcloud_load_balancer_target.type == "ip": - result["ip"] = to_native(self.hcloud_load_balancer_target.ip.ip) + result["ip"] = self.hcloud_load_balancer_target.ip.ip return result def _get_load_balancer_and_target(self): @@ -225,7 +224,8 @@ class AnsibleHCloudLoadBalancerTarget(AnsibleHCloud): if not self.module.check_mode: try: - self.hcloud_load_balancer.add_target(**params).wait_until_finished() + action = self.hcloud_load_balancer.add_target(**params) + action.wait_until_finished() except APIException as exception: if exception.code == "locked" or exception.code == "conflict": self._create_load_balancer_target() @@ -270,7 +270,8 @@ class AnsibleHCloudLoadBalancerTarget(AnsibleHCloud): use_private_ip=False, ) try: - self.hcloud_load_balancer.remove_target(target).wait_until_finished() + action = self.hcloud_load_balancer.remove_target(target) + action.wait_until_finished() except HCloudException as exception: self.fail_json_hcloud(exception) self._mark_as_changed() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_type_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_type_info.py index 67feafd59..90505651c 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_type_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_type_info.py @@ -88,7 +88,6 @@ hcloud_load_balancer_type_info: """ 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 @@ -104,18 +103,20 @@ class AnsibleHCloudLoadBalancerTypeInfo(AnsibleHCloud): 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, - } - ) + if load_balancer_type is None: + continue + + tmp.append( + { + "id": str(load_balancer_type.id), + "name": load_balancer_type.name, + "description": 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): diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/location_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/location_info.py index ac495c6c8..676917bfc 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/location_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/location_info.py @@ -78,7 +78,6 @@ hcloud_location_info: """ 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 @@ -94,16 +93,18 @@ class AnsibleHCloudLocationInfo(AnsibleHCloud): 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), - } - ) + if location is None: + continue + + tmp.append( + { + "id": str(location.id), + "name": location.name, + "description": location.description, + "city": location.city, + "country": location.country, + } + ) return tmp def get_locations(self): diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/network.py b/ansible_collections/hetzner/hcloud/plugins/modules/network.py index 24e45a48d..92fe9461e 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/network.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/network.py @@ -115,7 +115,6 @@ hcloud_network: """ 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 @@ -129,9 +128,9 @@ class AnsibleHCloudNetwork(AnsibleHCloud): 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), + "id": str(self.hcloud_network.id), + "name": self.hcloud_network.name, + "ip_range": 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, @@ -165,7 +164,8 @@ class AnsibleHCloudNetwork(AnsibleHCloud): delete_protection = self.module.params.get("delete_protection") if delete_protection is not None: self._get_network() - self.hcloud_network.change_protection(delete=delete_protection).wait_until_finished() + action = self.hcloud_network.change_protection(delete=delete_protection) + action.wait_until_finished() except HCloudException as exception: self.fail_json_hcloud(exception) self._mark_as_changed() @@ -173,6 +173,13 @@ class AnsibleHCloudNetwork(AnsibleHCloud): def _update_network(self): try: + name = self.module.params.get("name") + if name is not None and self.hcloud_network.name != name: + self.module.fail_on_missing_params(required_params=["id"]) + if not self.module.check_mode: + self.hcloud_network.update(name=name) + self._mark_as_changed() + labels = self.module.params.get("labels") if labels is not None and labels != self.hcloud_network.labels: if not self.module.check_mode: @@ -182,7 +189,8 @@ class AnsibleHCloudNetwork(AnsibleHCloud): ip_range = self.module.params.get("ip_range") if ip_range is not None and ip_range != self.hcloud_network.ip_range: if not self.module.check_mode: - self.hcloud_network.change_ip_range(ip_range=ip_range).wait_until_finished() + action = self.hcloud_network.change_ip_range(ip_range=ip_range) + action.wait_until_finished() self._mark_as_changed() expose_routes_to_vswitch = self.module.params.get("expose_routes_to_vswitch") @@ -197,7 +205,8 @@ class AnsibleHCloudNetwork(AnsibleHCloud): 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() + action = self.hcloud_network.change_protection(delete=delete_protection) + action.wait_until_finished() self._mark_as_changed() except HCloudException as exception: self.fail_json_hcloud(exception) diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/network_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/network_info.py index 4008352b4..8f1e5fbf7 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/network_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/network_info.py @@ -185,7 +185,6 @@ hcloud_network_info: """ 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 @@ -201,55 +200,55 @@ class AnsibleHCloudNetworkInfo(AnsibleHCloud): tmp = [] for network in self.hcloud_network_info: - if network is not None: - subnets = [] - for subnet in network.subnets: - prepared_subnet = { - "type": subnet.type, - "ip_range": subnet.ip_range, - "network_zone": subnet.network_zone, - "gateway": subnet.gateway, - } - subnets.append(prepared_subnet) - routes = [] - for route in network.routes: - prepared_route = {"destination": route.destination, "gateway": route.gateway} - routes.append(prepared_route) - - servers = [] - for server in network.servers: - image = None if server.image is None else to_native(server.image.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) - prepared_server = { - "id": to_native(server.id), - "name": to_native(server.name), - "ipv4_address": ipv4_address, - "ipv6": ipv6, - "image": image, - "server_type": to_native(server.server_type.name), - "datacenter": to_native(server.datacenter.name), - "location": to_native(server.datacenter.location.name), - "rescue_enabled": server.rescue_enabled, - "backup_window": to_native(server.backup_window), - "labels": server.labels, - "status": to_native(server.status), - } - 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, - "expose_routes_to_vswitch": network.expose_routes_to_vswitch, - "servers": servers, - "labels": network.labels, - "delete_protection": network.protection["delete"], - } - ) + if network is None: + continue + + subnets = [] + for subnet in network.subnets: + prepared_subnet = { + "type": subnet.type, + "ip_range": subnet.ip_range, + "network_zone": subnet.network_zone, + "gateway": subnet.gateway, + } + subnets.append(prepared_subnet) + + routes = [] + for route in network.routes: + prepared_route = {"destination": route.destination, "gateway": route.gateway} + routes.append(prepared_route) + + servers = [] + for server in network.servers: + prepared_server = { + "id": str(server.id), + "name": server.name, + "ipv4_address": server.public_net.ipv4.ip if server.public_net.ipv4 is not None else None, + "ipv6": server.public_net.ipv6.ip if server.public_net.ipv6 is not None else None, + "image": server.image.name if server.image is not None else None, + "server_type": server.server_type.name, + "datacenter": server.datacenter.name, + "location": server.datacenter.location.name, + "rescue_enabled": server.rescue_enabled, + "backup_window": server.backup_window, + "labels": server.labels, + "status": server.status, + } + servers.append(prepared_server) + + tmp.append( + { + "id": str(network.id), + "name": network.name, + "ip_range": 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): diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/placement_group.py b/ansible_collections/hetzner/hcloud/plugins/modules/placement_group.py index ba26fad22..45a0cd76b 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/placement_group.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/placement_group.py @@ -107,7 +107,6 @@ hcloud_placement_group: """ 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 @@ -121,10 +120,10 @@ class AnsibleHCloudPlacementGroup(AnsibleHCloud): def _prepare_result(self): return { - "id": to_native(self.hcloud_placement_group.id), - "name": to_native(self.hcloud_placement_group.name), + "id": str(self.hcloud_placement_group.id), + "name": self.hcloud_placement_group.name, "labels": self.hcloud_placement_group.labels, - "type": to_native(self.hcloud_placement_group.type), + "type": self.hcloud_placement_group.type, "servers": self.hcloud_placement_group.servers, } diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/primary_ip.py b/ansible_collections/hetzner/hcloud/plugins/modules/primary_ip.py index 607f6c7e1..08bcea493 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/primary_ip.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/primary_ip.py @@ -35,6 +35,12 @@ options: - Home Location of the Hetzner Cloud Primary IP. - Required if no I(server) is given and Primary IP does not exist. type: str + server: + description: + - Name or ID of the Hetzner Cloud Server the Primary IP should be assigned to. + - The Primary IP cannot be assigned to a running server. + - Required if no O(datacenter) is given and the Primary IP does not exist. + type: str type: description: - Type of the Primary IP. @@ -43,9 +49,9 @@ options: type: str auto_delete: description: - - Delete this Primary IP when the resource it is assigned to is deleted + - Delete the Primary IP when the resource it is assigned to is deleted. type: bool - default: no + default: false delete_protection: description: - Protect the Primary IP for deletion. @@ -66,22 +72,39 @@ extends_documentation_fragment: """ EXAMPLES = """ -- name: Create a basic IPv4 Primary IP +- name: Create a IPv4 Primary IP hetzner.hcloud.primary_ip: name: my-primary-ip datacenter: fsn1-dc14 type: ipv4 state: present -- name: Create a basic IPv6 Primary IP + +- name: Create a IPv6 Primary IP hetzner.hcloud.primary_ip: name: my-primary-ip datacenter: fsn1-dc14 type: ipv6 state: present -- name: Primary IP should be absent + +- name: Delete a Primary IP hetzner.hcloud.primary_ip: name: my-primary-ip state: absent + +- name: Ensure the server is stopped + hetzner.hcloud.server: + name: my-server + state: stopped +- name: Create a Primary IP attached to a Server + hetzner.hcloud.primary_ip: + name: my-primary-ip + server: my-server + type: ipv4 + state: present +- name: Ensure the server is started + hetzner.hcloud.server: + name: my-server + state: started """ RETURN = """ @@ -127,10 +150,24 @@ hcloud_primary_ip: sample: key: value mylabel: 123 + assignee_id: + description: ID of the resource the Primary IP is assigned to, null if it is not assigned. + type: int + returned: always + sample: 1937415 + assignee_type: + description: Resource type the Primary IP can be assigned to. + type: str + returned: always + sample: server + auto_delete: + description: Delete the Primary IP when the resource it is assigned to is deleted. + type: bool + returned: always + sample: false """ 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 @@ -144,13 +181,18 @@ class AnsibleHCloudPrimaryIP(AnsibleHCloud): def _prepare_result(self): return { - "id": to_native(self.hcloud_primary_ip.id), - "name": to_native(self.hcloud_primary_ip.name), - "ip": to_native(self.hcloud_primary_ip.ip), - "type": to_native(self.hcloud_primary_ip.type), - "datacenter": to_native(self.hcloud_primary_ip.datacenter.name), + "id": str(self.hcloud_primary_ip.id), + "name": self.hcloud_primary_ip.name, + "ip": self.hcloud_primary_ip.ip, + "type": self.hcloud_primary_ip.type, + "datacenter": self.hcloud_primary_ip.datacenter.name, "labels": self.hcloud_primary_ip.labels, "delete_protection": self.hcloud_primary_ip.protection["delete"], + "assignee_id": ( + str(self.hcloud_primary_ip.assignee_id) if self.hcloud_primary_ip.assignee_id is not None else None + ), + "assignee_type": self.hcloud_primary_ip.assignee_type, + "auto_delete": self.hcloud_primary_ip.auto_delete, } def _get_primary_ip(self): @@ -163,23 +205,35 @@ class AnsibleHCloudPrimaryIP(AnsibleHCloud): self.fail_json_hcloud(exception) def _create_primary_ip(self): - self.module.fail_on_missing_params(required_params=["type", "datacenter"]) + self.fail_on_invalid_params( + required=["type", "name"], + required_one_of=[["server", "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")), + "auto_delete": self.module.params.get("auto_delete"), + "datacenter": None, # TODO: https://github.com/hetznercloud/hcloud-python/pull/363 } + if self.module.params.get("datacenter") is not None: + params["datacenter"] = self.client.datacenters.get_by_name(self.module.params.get("datacenter")) + elif self.module.params.get("server") is not None: + params["assignee_id"] = self._client_get_by_name_or_id("servers", self.module.params.get("server")).id + if self.module.params.get("labels") is not None: params["labels"] = self.module.params.get("labels") if not self.module.check_mode: resp = self.client.primary_ips.create(**params) + if resp.action is not None: + resp.action.wait_until_finished() self.hcloud_primary_ip = resp.primary_ip 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() + action = self.hcloud_primary_ip.change_protection(delete=delete_protection) + action.wait_until_finished() except HCloudException as exception: self.fail_json_hcloud(exception) self._mark_as_changed() @@ -187,16 +241,26 @@ class AnsibleHCloudPrimaryIP(AnsibleHCloud): def _update_primary_ip(self): try: + changes = {} + + auto_delete = self.module.params.get("auto_delete") + if auto_delete is not None and auto_delete != self.hcloud_primary_ip.auto_delete: + changes["auto_delete"] = auto_delete + labels = self.module.params.get("labels") if labels is not None and labels != self.hcloud_primary_ip.labels: + changes["labels"] = labels + + if changes: if not self.module.check_mode: - self.hcloud_primary_ip.update(labels=labels) + self.hcloud_primary_ip.update(**changes) self._mark_as_changed() delete_protection = self.module.params.get("delete_protection") if delete_protection is not None and delete_protection != self.hcloud_primary_ip.protection["delete"]: if not self.module.check_mode: - self.hcloud_primary_ip.change_protection(delete=delete_protection).wait_until_finished() + action = self.hcloud_primary_ip.change_protection(delete=delete_protection) + action.wait_until_finished() self._mark_as_changed() self._get_primary_ip() @@ -228,6 +292,7 @@ class AnsibleHCloudPrimaryIP(AnsibleHCloud): id={"type": "int"}, name={"type": "str"}, datacenter={"type": "str"}, + server={"type": "str"}, auto_delete={"type": "bool", "default": False}, type={"choices": ["ipv4", "ipv6"]}, labels={"type": "dict"}, diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/primary_ip_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/primary_ip_info.py index c0bfdbb35..72e359ae2 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/primary_ip_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/primary_ip_info.py @@ -117,10 +117,14 @@ hcloud_primary_ip_info: description: True if the Primary IP is protected for deletion returned: always type: bool + auto_delete: + description: Delete the Primary IP when the resource it is assigned to is deleted. + type: bool + returned: always + sample: false """ 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 @@ -136,26 +140,24 @@ class AnsibleHCloudPrimaryIPInfo(AnsibleHCloud): 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"], - } - ) + if primary_ip is None: + continue + + tmp.append( + { + "id": str(primary_ip.id), + "name": primary_ip.name, + "ip": primary_ip.ip, + "type": primary_ip.type, + "assignee_id": str(primary_ip.assignee_id) if primary_ip.assignee_id is not None else None, + "assignee_type": primary_ip.assignee_type, + "auto_delete": primary_ip.auto_delete, + "home_location": primary_ip.datacenter.name, + "dns_ptr": primary_ip.dns_ptr[0]["dns_ptr"] if len(primary_ip.dns_ptr) else None, + "labels": primary_ip.labels, + "delete_protection": primary_ip.protection["delete"], + } + ) return tmp diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/rdns.py b/ansible_collections/hetzner/hcloud/plugins/modules/rdns.py index b2decdec8..4e21f3e92 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/rdns.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/rdns.py @@ -136,7 +136,6 @@ import ipaddress from typing import Any 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 @@ -157,18 +156,18 @@ class AnsibleHCloudReverseDNS(AnsibleHCloud): "server": None, "floating_ip": None, "load_balancer": None, - "ip_address": to_native(self.hcloud_rdns["ip_address"]), - "dns_ptr": to_native(self.hcloud_rdns["dns_ptr"]), + "ip_address": self.hcloud_rdns["ip_address"], + "dns_ptr": self.hcloud_rdns["dns_ptr"], } if self.module.params.get("server"): - result["server"] = to_native(self.hcloud_resource.name) + result["server"] = self.hcloud_resource.name elif self.module.params.get("floating_ip"): - result["floating_ip"] = to_native(self.hcloud_resource.name) + result["floating_ip"] = self.hcloud_resource.name elif self.module.params.get("load_balancer"): - result["load_balancer"] = to_native(self.hcloud_resource.name) + result["load_balancer"] = self.hcloud_resource.name elif self.module.params.get("primary_ip"): - result["primary_ip"] = to_native(self.hcloud_resource.name) + result["primary_ip"] = self.hcloud_resource.name return result def _get_resource(self): @@ -277,7 +276,8 @@ class AnsibleHCloudReverseDNS(AnsibleHCloud): if not self.module.check_mode: try: - self.hcloud_resource.change_dns_ptr(**params).wait_until_finished() + action = self.hcloud_resource.change_dns_ptr(**params) + action.wait_until_finished() except HCloudException as exception: self.fail_json_hcloud(exception) self._mark_as_changed() @@ -294,7 +294,8 @@ class AnsibleHCloudReverseDNS(AnsibleHCloud): if not self.module.check_mode: try: - self.hcloud_resource.change_dns_ptr(**params).wait_until_finished() + action = self.hcloud_resource.change_dns_ptr(**params) + action.wait_until_finished() except HCloudException as exception: self.fail_json_hcloud(exception) self._mark_as_changed() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/route.py b/ansible_collections/hetzner/hcloud/plugins/modules/route.py index 3c96a7382..37ff50b07 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/route.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/route.py @@ -86,7 +86,6 @@ hcloud_route: """ 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 @@ -101,8 +100,8 @@ class AnsibleHCloudRoute(AnsibleHCloud): def _prepare_result(self): return { - "network": to_native(self.hcloud_network.name), - "destination": to_native(self.hcloud_route.destination), + "network": self.hcloud_network.name, + "destination": self.hcloud_route.destination, "gateway": self.hcloud_route.gateway, } @@ -130,7 +129,8 @@ class AnsibleHCloudRoute(AnsibleHCloud): if not self.module.check_mode: try: - self.hcloud_network.add_route(route=route).wait_until_finished() + action = self.hcloud_network.add_route(route=route) + action.wait_until_finished() except HCloudException as exception: self.fail_json_hcloud(exception) @@ -150,7 +150,8 @@ class AnsibleHCloudRoute(AnsibleHCloud): if self.hcloud_route is not None and self.hcloud_network is not None: if not self.module.check_mode: try: - self.hcloud_network.delete_route(self.hcloud_route).wait_until_finished() + action = self.hcloud_network.delete_route(self.hcloud_route) + action.wait_until_finished() except HCloudException as exception: self.fail_json_hcloud(exception) self._mark_as_changed() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/server.py b/ansible_collections/hetzner/hcloud/plugins/modules/server.py index f5cadb807..d7bae3fc1 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/server.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/server.py @@ -22,50 +22,58 @@ author: options: id: description: - - The ID of the Hetzner Cloud server to manage. - - Only required if no server I(name) is given + - ID of the Hetzner Cloud Server to manage. + - Only required if no server O(name) is given type: int name: description: - - The Name of the Hetzner Cloud server to manage. - - Only required if no server I(id) is given or a server does not exist. + - Name of the Hetzner Cloud Server to manage. + - Only required if no server O(id) is given or a server does not exist. type: str server_type: description: - - The Server Type of the Hetzner Cloud server to manage. + - Hetzner Cloud Server Type (name or ID) of the server. - Required if server does not exist. type: str ssh_keys: description: - - List of SSH key names - - The key names correspond to the SSH keys configured for your - Hetzner Cloud account access. + - List of Hetzner Cloud SSH Keys (name or ID) to create the server with. + - Only used during the server creation. type: list elements: str volumes: description: - - List of Volumes IDs that should be attached to the server on server creation. + - List of Hetzner Cloud Volumes (name or ID) that should be attached to the server. + - Only used during the server creation. type: list elements: str firewalls: description: - - List of Firewall IDs that should be attached to the server on server creation. + - List of Hetzner Cloud Firewalls (name or ID) that should be attached to the server. type: list elements: str image: description: - - Image the server should be created from. - - Required if server does not exist. + - Hetzner Cloud Image (name or ID) to create the server from. + - Required if server does not exist or when O(state=rebuild). type: str + image_allow_deprecated: + description: + - Allows the creation of servers with deprecated images. + type: bool + default: false + aliases: [allow_deprecated_image] location: description: - - Location of Server. - - Required if no I(datacenter) is given and server does not exist. + - Hetzner Cloud Location (name or ID) to create the server in. + - Required if no O(datacenter) is given and server does not exist. + - Only used during the server creation. type: str datacenter: description: - - Datacenter of Server. - - Required if no I(location) is given and server does not exist. + - Hetzner Cloud Datacenter (name or ID) to create the server in. + - Required if no O(location) is given and server does not exist. + - Only used during the server creation. type: str backups: description: @@ -79,50 +87,42 @@ options: default: false enable_ipv4: description: - - Enables the public ipv4 address + - Enables the public ipv4 address. type: bool default: true enable_ipv6: description: - - Enables the public ipv6 address + - Enables the public ipv6 address. type: bool 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 + - Hetzner Cloud Primary IPv4 (name or ID) to use. + - If omitted and O(enable_ipv4=true), a new ipv4 Primary IP will automatically be created. type: str ipv6: description: - - ID of the ipv6 Primary IP to use. If omitted and enable_ipv6 is true, a new ipv6 Primary IP will automatically be created. + - Hetzner Cloud Primary IPv6 (name or ID) to use. + - If omitted and O(enable_ipv6=true), a new ipv6 Primary IP will automatically be created. type: str private_networks: description: - - List of private networks the server is attached to (name or ID) + - List of Hetzner Cloud Networks (name or ID) the server should be attached to. - If None, private networks are left as they are (e.g. if previously added by hcloud_server_network), if it has any other value (including []), only those networks are attached to the server. type: list elements: str - force_upgrade: - description: - - Deprecated - - Force the upgrade of the server. - - Power off the server if it is running on upgrade. - type: bool force: description: - Force the update of the server. - - May power off the server if update. - type: bool - default: false - allow_deprecated_image: - description: - - Allows the creation of servers with deprecated images. + - May power off the server if update is applied. type: bool default: false + aliases: [force_upgrade] user_data: description: - User Data to be passed to the server on creation. - - Only used if server does not exist. + - Only used during the server creation. type: str rescue_mode: description: @@ -135,16 +135,16 @@ options: delete_protection: description: - Protect the Server for deletion. - - Needs to be the same as I(rebuild_protection). + - Needs to be the same as O(rebuild_protection). type: bool rebuild_protection: description: - Protect the Server for rebuild. - - Needs to be the same as I(delete_protection). + - Needs to be the same as O(delete_protection). type: bool placement_group: description: - - Placement Group of the server. + - Hetzner Cloud Placement Group (name or ID) to create the server in. type: str state: description: @@ -336,9 +336,9 @@ hcloud_server: """ from datetime import datetime, timedelta, timezone +from typing import TYPE_CHECKING, Literal 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 @@ -348,8 +348,14 @@ from ..module_utils.vendor.hcloud.servers import ( Server, ServerCreatePublicNetwork, ) -from ..module_utils.vendor.hcloud.ssh_keys import SSHKey -from ..module_utils.vendor.hcloud.volumes import Volume + +if TYPE_CHECKING: + from ..module_utils.vendor.hcloud.actions import BoundAction + from ..module_utils.vendor.hcloud.firewalls import BoundFirewall + from ..module_utils.vendor.hcloud.networks import BoundNetwork + from ..module_utils.vendor.hcloud.placement_groups import BoundPlacementGroup + from ..module_utils.vendor.hcloud.primary_ips import PrimaryIP + from ..module_utils.vendor.hcloud.server_types import ServerType class AnsibleHCloudServer(AnsibleHCloud): @@ -358,38 +364,31 @@ class AnsibleHCloudServer(AnsibleHCloud): 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) - ) - 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) - ) 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], + "id": str(self.hcloud_server.id), + "name": self.hcloud_server.name, + "created": self.hcloud_server.created.isoformat(), + "ipv4_address": ( + self.hcloud_server.public_net.ipv4.ip if self.hcloud_server.public_net.ipv4 is not None else None + ), + "ipv6": self.hcloud_server.public_net.ipv6.ip if self.hcloud_server.public_net.ipv6 is not None else None, + "private_networks": [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 + {"name": 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), - "location": to_native(self.hcloud_server.datacenter.location.name), - "placement_group": placement_group, + "image": self.hcloud_server.image.name if self.hcloud_server.image is not None else None, + "server_type": self.hcloud_server.server_type.name, + "datacenter": self.hcloud_server.datacenter.name, + "location": self.hcloud_server.datacenter.location.name, + "placement_group": ( + self.hcloud_server.placement_group.name if self.hcloud_server.placement_group is not None else None + ), "rescue_enabled": self.hcloud_server.rescue_enabled, - "backup_window": backup_window, + "backup_window": self.hcloud_server.backup_window, "labels": self.hcloud_server.labels, "delete_protection": self.hcloud_server.protection["delete"], "rebuild_protection": self.hcloud_server.protection["rebuild"], - "status": to_native(self.hcloud_server.status), + "status": self.hcloud_server.status, } def _get_server(self): @@ -405,73 +404,76 @@ class AnsibleHCloudServer(AnsibleHCloud): self.module.fail_on_missing_params(required_params=["name", "server_type", "image"]) server_type = self._get_server_type() + image = self._get_image(server_type) params = { "name": self.module.params.get("name"), + "labels": self.module.params.get("labels"), "server_type": server_type, + "image": image, "user_data": self.module.params.get("user_data"), - "labels": self.module.params.get("labels"), - "image": self._get_image(server_type), - "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"), ), } + if self.module.params.get("placement_group") is not None: + params["placement_group"] = self._client_get_by_name_or_id( + "placement_groups", self.module.params.get("placement_group") + ) + if self.module.params.get("ipv4") is not None: - 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 + params["public_net"].ipv4 = self._client_get_by_name_or_id("primary_ips", self.module.params.get("ipv4")) if self.module.params.get("ipv6") is not None: - 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 + params["public_net"].ipv6 = self._client_get_by_name_or_id("primary_ips", self.module.params.get("ipv6")) if self.module.params.get("private_networks") is not None: - _networks = [] - for network_name_or_id in self.module.params.get("private_networks"): - _networks.append( - self.client.networks.get_by_name(network_name_or_id) - or self.client.networks.get_by_id(network_name_or_id) - ) - params["networks"] = _networks + params["networks"] = [ + self._client_get_by_name_or_id("networks", name_or_id) + for name_or_id in self.module.params.get("private_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"] = [ + self._client_get_by_name_or_id("ssh_keys", name_or_id) + for name_or_id 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"] = [ + self._client_get_by_name_or_id("volumes", name_or_id) + for name_or_id in self.module.params.get("volumes") + ] + if self.module.params.get("firewalls") is not None: - params["firewalls"] = [] - 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(firewall) - else: - params["firewalls"].append(self.client.firewalls.get_by_id(firewall_param)) + params["firewalls"] = [ + self._client_get_by_name_or_id("firewalls", name_or_id) + for name_or_id in self.module.params.get("firewalls") + ] 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_get_by_name_or_id("locations", 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_get_by_name_or_id("datacenters", self.module.params.get("datacenter")) if self.module.params.get("state") == "stopped": params["start_after_create"] = False + if not self.module.check_mode: try: resp = self.client.servers.create(**params) self.result["root_password"] = resp.root_password - resp.action.wait_until_finished(max_retries=1000) - [action.wait_until_finished() for action in resp.next_actions] + # Action should take 60 to 90 seconds on average, but can be >10m when creating a + # server from a custom images + resp.action.wait_until_finished(max_retries=1800) + for action in resp.next_actions: + action.wait_until_finished() rescue_mode = self.module.params.get("rescue_mode") if rescue_mode: @@ -481,40 +483,35 @@ class AnsibleHCloudServer(AnsibleHCloud): backups = self.module.params.get("backups") if backups: self._get_server() - self.hcloud_server.enable_backup().wait_until_finished() + action = self.hcloud_server.enable_backup() + action.wait_until_finished() 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: self._get_server() - self.hcloud_server.change_protection( + action = self.hcloud_server.change_protection( delete=delete_protection, rebuild=rebuild_protection, - ).wait_until_finished() + ) + action.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( + def _get_image(self, server_type: ServerType): + image = self.client.images.get_by_name_and_architecture( 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 - image = images[0] - else: - try: - image = self.client.images.get_by_id(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 is None: + image = self.client.images.get_by_id(self.module.params.get("image")) + if image.deprecated is not None: available_until = image.deprecated + timedelta(days=90) - if self.module.params.get("allow_deprecated_image"): + if self.module.params.get("image_allow_deprecated"): self.module.warn( 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')}." @@ -524,27 +521,18 @@ class AnsibleHCloudServer(AnsibleHCloud): 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." + "If you want to use this image use image_allow_deprecated=true." ) ) return image - def _get_server_type(self): - 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 HCloudException as exception: - self.fail_json_hcloud( - exception, - msg=f"server_type {self.module.params.get('server_type')} was not found", - ) + def _get_server_type(self) -> ServerType: + server_type = self._client_get_by_name_or_id("server_types", self.module.params.get("server_type")) self._check_and_warn_deprecated_server(server_type) - return server_type - def _check_and_warn_deprecated_server(self, server_type): + def _check_and_warn_deprecated_server(self, server_type: ServerType) -> None: if server_type.deprecation is None: return @@ -567,42 +555,16 @@ class AnsibleHCloudServer(AnsibleHCloud): "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")) - if placement_group is None: - try: - placement_group = self.client.placement_groups.get_by_id(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 - - def _get_primary_ip(self, field): - if self.module.params.get(field) is None: - return None - - 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 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 and self.module.params.get("force_upgrade") is not None: - self.module.warn("force_upgrade is deprecated, use force instead") - + def _update_server(self) -> None: try: previous_server_status = self.hcloud_server.status + labels = self.module.params.get("labels") + if labels is not None and labels != self.hcloud_server.labels: + if not self.module.check_mode: + self.hcloud_server.update(labels=labels) + self._mark_as_changed() + rescue_mode = self.module.params.get("rescue_mode") if rescue_mode and self.hcloud_server.rescue_enabled is False: if not self.module.check_mode: @@ -610,167 +572,39 @@ class AnsibleHCloudServer(AnsibleHCloud): self._mark_as_changed() elif not rescue_mode and self.hcloud_server.rescue_enabled is True: if not self.module.check_mode: - self.hcloud_server.disable_rescue().wait_until_finished() + action = self.hcloud_server.disable_rescue() + action.wait_until_finished() self._mark_as_changed() backups = self.module.params.get("backups") if backups and self.hcloud_server.backup_window is None: if not self.module.check_mode: - self.hcloud_server.enable_backup().wait_until_finished() + action = self.hcloud_server.enable_backup() + action.wait_until_finished() self._mark_as_changed() elif backups is not None and not backups and self.hcloud_server.backup_window is not None: if not self.module.check_mode: - self.hcloud_server.disable_backup().wait_until_finished() + action = self.hcloud_server.disable_backup() + action.wait_until_finished() self._mark_as_changed() - labels = self.module.params.get("labels") - if labels is not None and labels != self.hcloud_server.labels: - if not self.module.check_mode: - self.hcloud_server.update(labels=labels) - self._mark_as_changed() + if self.module.params.get("firewalls") is not None: + self._update_server_firewalls() - wanted_firewalls = self.module.params.get("firewalls") - if wanted_firewalls is not None: - # Removing existing but not wanted firewalls - for current_firewall in self.hcloud_server.public_net.firewalls: - if current_firewall.firewall.name not in wanted_firewalls: - self._mark_as_changed() - 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 firewall_name in wanted_firewalls: - found = False - 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: - 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: - if not self.module.check_mode: - self.hcloud_server.remove_from_placement_group().wait_until_finished() - 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 - ): - self.stop_server_if_forced() - if not self.module.check_mode: - self.hcloud_server.add_to_placement_group(placement_group).wait_until_finished() - self._mark_as_changed() - - 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.stop_server_if_forced() - if not self.module.check_mode: - self.hcloud_server.public_net.primary_ipv4.unassign().wait_until_finished() - 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 - ): - self.stop_server_if_forced() - if not self.module.check_mode: - if self.hcloud_server.public_net.primary_ipv4: - self.hcloud_server.public_net.primary_ipv4.unassign().wait_until_finished() - primary_ip.assign(self.hcloud_server.id, "server").wait_until_finished() - 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.stop_server_if_forced() - if not self.module.check_mode: - self.hcloud_server.public_net.primary_ipv6.unassign().wait_until_finished() - 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 - ): - self.stop_server_if_forced() - if not self.module.check_mode: - if self.hcloud_server.public_net.primary_ipv6 is not None: - self.hcloud_server.public_net.primary_ipv6.unassign().wait_until_finished() - primary_ip.assign(self.hcloud_server.id, "server").wait_until_finished() - self._mark_as_changed() - if "private_networks" in self.module.params and self.module.params["private_networks"] is not None: - if not bool(self.module.params["private_networks"]): - # This handles None, "" and [] - networks_target = {} - 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}) - networks_target = _networks - networks_is = dict() - for p_network in self.hcloud_server.private_net: - networks_is.update({p_network.network.id: p_network.network}) - for network_id in set(list(networks_is) + list(networks_target)): - if network_id in networks_is and network_id not in networks_target: - self.stop_server_if_forced() - if not self.module.check_mode: - self.hcloud_server.detach_from_network(networks_is[network_id]).wait_until_finished() - self._mark_as_changed() - elif network_id in networks_target and network_id not in networks_is: - self.stop_server_if_forced() - if not self.module.check_mode: - self.hcloud_server.attach_to_network(networks_target[network_id]).wait_until_finished() - self._mark_as_changed() - - server_type = self.module.params.get("server_type") - 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) - - 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 self.module.params.get("placement_group") is not None: + self._update_server_placement_group() + + if self.module.params.get("ipv4") is not None: + self._update_server_ip("ipv4") + + if self.module.params.get("ipv6") is not None: + self._update_server_ip("ipv6") + + if self.module.params.get("private_networks") is not None: + self._update_server_networks() + + if self.module.params.get("server_type") is not None: + self._update_server_server_type() if not self.module.check_mode and ( (self.module.params.get("state") == "present" and previous_server_status == Server.STATUS_RUNNING) @@ -785,15 +619,197 @@ class AnsibleHCloudServer(AnsibleHCloud): or rebuild_protection != self.hcloud_server.protection["rebuild"] ): if not self.module.check_mode: - self.hcloud_server.change_protection( + action = self.hcloud_server.change_protection( delete=delete_protection, rebuild=rebuild_protection, - ).wait_until_finished() + ) + action.wait_until_finished() self._mark_as_changed() self._get_server() except HCloudException as exception: self.fail_json_hcloud(exception) + def _update_server_placement_group(self) -> None: + current: BoundPlacementGroup | None = self.hcloud_server.placement_group + wanted = self.module.params.get("placement_group") + + # Return if nothing changed + if current is not None and current.has_id_or_name(wanted): + return + + # Fetch resource if parameter is truthy + if wanted: + placement_group = self._client_get_by_name_or_id("placement_groups", wanted) + + # Remove if current is defined + if current is not None: + if not self.module.check_mode: + action = self.hcloud_server.remove_from_placement_group() + action.wait_until_finished() + self._mark_as_changed() + + # Return if parameter is falsy + if not wanted: + return + + # Assign new + self.stop_server_if_forced() + if not self.module.check_mode: + action = self.hcloud_server.add_to_placement_group(placement_group) + action.wait_until_finished() + self._mark_as_changed() + + def _update_server_server_type(self) -> None: + current: ServerType = self.hcloud_server.server_type + wanted = self.module.params.get("server_type") + + # Return if nothing changed + if current.has_id_or_name(wanted): + # Check if we should warn for using an deprecated server type + self._check_and_warn_deprecated_server(self.hcloud_server.server_type) + return + + self.stop_server_if_forced() + + upgrade_disk = self.module.params.get("upgrade_disk") + # Upgrading a server takes 160 seconds on average, upgrading the disk should + # take more time + upgrade_timeout = 600 if upgrade_disk else 180 + + if not self.module.check_mode: + action = self.hcloud_server.change_type( + server_type=self._get_server_type(), + upgrade_disk=upgrade_disk, + ) + action.wait_until_finished(max_retries=upgrade_timeout) + self._mark_as_changed() + + def _update_server_ip(self, kind: Literal["ipv4", "ipv6"]) -> None: + current: PrimaryIP | None = getattr(self.hcloud_server.public_net, f"primary_{kind}") + wanted = self.module.params.get(kind) + enable = self.module.params.get(f"enable_{kind}") + + # Return if nothing changed + if current is not None and current.has_id_or_name(wanted) and enable: + return + + # Fetch resource if parameter is truthy + if wanted: + primary_ip = self._client_get_by_name_or_id("primary_ips", wanted) + + # Remove if current is defined + if current is not None: + self.stop_server_if_forced() + if not self.module.check_mode: + action = self.client.primary_ips.unassign(current) + action.wait_until_finished() + self._mark_as_changed() + + # Return if parameter is falsy or resource is disabled + if not wanted or not enable: + return + + # Assign new + self.stop_server_if_forced() + if not self.module.check_mode: + action = self.client.primary_ips.assign( + primary_ip, + assignee_id=self.hcloud_server.id, + assignee_type="server", + ) + action.wait_until_finished() + self._mark_as_changed() + + def _update_server_networks(self) -> None: + current: list[BoundNetwork] = [item.network for item in self.hcloud_server.private_net] + wanted: list[BoundNetwork] = [ + self._client_get_by_name_or_id("networks", name_or_id) + for name_or_id in self.module.params.get("private_networks") + ] + + current_ids = {item.id for item in current} + wanted_ids = {item.id for item in wanted} + + # Removing existing but not wanted networks + actions: list[BoundAction] = [] + for current_network in current: + if current_network.id in wanted_ids: + continue + + self._mark_as_changed() + if self.module.check_mode: + continue + + actions.append(self.hcloud_server.detach_from_network(current_network)) + + for action in actions: + action.wait_until_finished() + + # Adding wanted networks that doesn't exist yet + actions: list[BoundAction] = [] + for wanted_network in wanted: + if wanted_network.id in current_ids: + continue + + self._mark_as_changed() + if self.module.check_mode: + continue + + actions.append(self.hcloud_server.attach_to_network(wanted_network)) + + for action in actions: + action.wait_until_finished() + + def _update_server_firewalls(self) -> None: + current: list[BoundFirewall] = [item.firewall for item in self.hcloud_server.public_net.firewalls] + wanted: list[BoundFirewall] = [ + self._client_get_by_name_or_id("firewalls", name_or_id) + for name_or_id in self.module.params.get("firewalls") + ] + + current_ids = {item.id for item in current} + wanted_ids = {item.id for item in wanted} + + # Removing existing but not wanted firewalls + actions: list[BoundAction] = [] + for current_firewall in current: + if current_firewall.id in wanted_ids: + continue + + self._mark_as_changed() + if self.module.check_mode: + continue + + actions.extend( + self.client.firewalls.remove_from_resources( + current_firewall, + [FirewallResource(type="server", server=self.hcloud_server)], + ) + ) + + for action in actions: + action.wait_until_finished() + + # Adding wanted firewalls that doesn't exist yet + actions: list[BoundAction] = [] + for wanted_firewall in wanted: + if wanted_firewall.id in current_ids: + continue + + self._mark_as_changed() + if self.module.check_mode: + continue + + actions.extend( + self.client.firewalls.apply_to_resources( + wanted_firewall, + [FirewallResource(type="server", server=self.hcloud_server)], + ) + ) + + for action in actions: + action.wait_until_finished() + def _set_rescue_mode(self, rescue_mode): if self.module.params.get("ssh_keys"): resp = self.hcloud_server.enable_rescue( @@ -813,7 +829,8 @@ class AnsibleHCloudServer(AnsibleHCloud): if self.hcloud_server: if self.hcloud_server.status != Server.STATUS_RUNNING: if not self.module.check_mode: - self.client.servers.power_on(self.hcloud_server).wait_until_finished() + action = self.client.servers.power_on(self.hcloud_server) + action.wait_until_finished() self._mark_as_changed() self._get_server() except HCloudException as exception: @@ -824,7 +841,8 @@ class AnsibleHCloudServer(AnsibleHCloud): if self.hcloud_server: if self.hcloud_server.status != Server.STATUS_OFF: if not self.module.check_mode: - self.client.servers.power_off(self.hcloud_server).wait_until_finished() + action = self.client.servers.power_off(self.hcloud_server) + action.wait_until_finished() self._mark_as_changed() self._get_server() except HCloudException as exception: @@ -833,18 +851,14 @@ class AnsibleHCloudServer(AnsibleHCloud): 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" - ): + if 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( - f"You can not upgrade a running instance {self.hcloud_server.name}. " - "You need to stop the instance or use force=true." - ) + + self.module.warn( + f"You can not upgrade a running instance {self.hcloud_server.name}. " + "You need to stop the instance or use force=true." + ) return None @@ -874,7 +888,8 @@ class AnsibleHCloudServer(AnsibleHCloud): self._get_server() if self.hcloud_server is not None: if not self.module.check_mode: - self.client.servers.delete(self.hcloud_server).wait_until_finished() + action = self.client.servers.delete(self.hcloud_server) + action.wait_until_finished() self._mark_as_changed() self.hcloud_server = None except HCloudException as exception: @@ -887,6 +902,7 @@ class AnsibleHCloudServer(AnsibleHCloud): id={"type": "int"}, name={"type": "str"}, image={"type": "str"}, + image_allow_deprecated={"type": "bool", "default": False, "aliases": ["allow_deprecated_image"]}, server_type={"type": "str"}, location={"type": "str"}, datacenter={"type": "str"}, @@ -902,9 +918,14 @@ class AnsibleHCloudServer(AnsibleHCloud): ipv4={"type": "str"}, ipv6={"type": "str"}, private_networks={"type": "list", "elements": "str", "default": None}, - force={"type": "bool", "default": False}, - force_upgrade={"type": "bool"}, - allow_deprecated_image={"type": "bool", "default": False}, + force={ + "type": "bool", + "default": False, + "aliases": ["force_upgrade"], + "deprecated_aliases": [ + {"collection_name": "hetzner.hcloud", "name": "force_upgrade", "version": "4.0.0"} + ], + }, rescue_mode={"type": "str"}, delete_protection={"type": "bool"}, rebuild_protection={"type": "bool"}, diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/server_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/server_info.py index cee1634cb..3417e58af 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/server_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/server_info.py @@ -146,7 +146,6 @@ hcloud_server_info: """ 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 @@ -162,36 +161,31 @@ class AnsibleHCloudServerInfo(AnsibleHCloud): 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), - "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"], - } - ) + if server is None: + continue + + tmp.append( + { + "id": str(server.id), + "name": server.name, + "created": server.created.isoformat(), + "ipv4_address": server.public_net.ipv4.ip if server.public_net.ipv4 is not None else None, + "ipv6": server.public_net.ipv6.ip if server.public_net.ipv6 is not None else None, + "private_networks": [net.network.name for net in server.private_net], + "private_networks_info": [{"name": net.network.name, "ip": net.ip} for net in server.private_net], + "image": server.image.name if server.image is not None else None, + "server_type": server.server_type.name, + "datacenter": server.datacenter.name, + "location": server.datacenter.location.name, + "placement_group": server.placement_group.name if server.placement_group is not None else None, + "rescue_enabled": server.rescue_enabled, + "backup_window": server.backup_window, + "labels": server.labels, + "status": server.status, + "delete_protection": server.protection["delete"], + "rebuild_protection": server.protection["rebuild"], + } + ) return tmp def get_servers(self): diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/server_network.py b/ansible_collections/hetzner/hcloud/plugins/modules/server_network.py index ca80a8a76..bc6dec428 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/server_network.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/server_network.py @@ -111,7 +111,6 @@ hcloud_server_network: """ 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 @@ -128,10 +127,10 @@ class AnsibleHCloudServerNetwork(AnsibleHCloud): def _prepare_result(self): return { - "network": to_native(self.hcloud_network.name), - "server": to_native(self.hcloud_server.name), - "ip": to_native(self.hcloud_server_network.ip), - "alias_ips": self.hcloud_server_network.alias_ips, + "network": self.hcloud_network.name, + "server": self.hcloud_server.name, + "ip": self.hcloud_server_network.ip, + "alias_ips": list(sorted(self.hcloud_server_network.alias_ips)), } def _get_server_and_network(self): @@ -165,7 +164,8 @@ class AnsibleHCloudServerNetwork(AnsibleHCloud): if not self.module.check_mode: try: - self.hcloud_server.attach_to_network(**params).wait_until_finished() + action = self.hcloud_server.attach_to_network(**params) + action.wait_until_finished() except HCloudException as exception: self.fail_json_hcloud(exception) @@ -183,7 +183,8 @@ class AnsibleHCloudServerNetwork(AnsibleHCloud): if not self.module.check_mode: try: - self.hcloud_server.change_alias_ips(**params).wait_until_finished() + action = self.hcloud_server.change_alias_ips(**params) + action.wait_until_finished() except APIException as exception: self.fail_json_hcloud(exception) @@ -205,7 +206,8 @@ class AnsibleHCloudServerNetwork(AnsibleHCloud): if self.hcloud_server_network is not None and self.hcloud_server is not None: if not self.module.check_mode: try: - self.hcloud_server.detach_from_network(self.hcloud_server_network.network).wait_until_finished() + action = self.hcloud_server.detach_from_network(self.hcloud_server_network.network) + action.wait_until_finished() except HCloudException as exception: self.fail_json_hcloud(exception) self._mark_as_changed() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/server_type_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/server_type_info.py index 61f1f5011..4f16b52fe 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/server_type_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/server_type_info.py @@ -124,7 +124,6 @@ hcloud_server_type_info: """ 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 @@ -140,29 +139,31 @@ class AnsibleHCloudServerTypeInfo(AnsibleHCloud): 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 - ), - } - ) + if server_type is None: + continue + + tmp.append( + { + "id": str(server_type.id), + "name": server_type.name, + "description": server_type.description, + "cores": server_type.cores, + "memory": server_type.memory, + "disk": server_type.disk, + "storage_type": server_type.storage_type, + "cpu_type": server_type.cpu_type, + "architecture": 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): diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/ssh_key.py b/ansible_collections/hetzner/hcloud/plugins/modules/ssh_key.py index 349c52c68..e33987c44 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/ssh_key.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/ssh_key.py @@ -113,7 +113,6 @@ hcloud_ssh_key: """ 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 @@ -127,10 +126,10 @@ class AnsibleHCloudSSHKey(AnsibleHCloud): def _prepare_result(self): return { - "id": to_native(self.hcloud_ssh_key.id), - "name": to_native(self.hcloud_ssh_key.name), - "fingerprint": to_native(self.hcloud_ssh_key.fingerprint), - "public_key": to_native(self.hcloud_ssh_key.public_key), + "id": str(self.hcloud_ssh_key.id), + "name": self.hcloud_ssh_key.name, + "fingerprint": self.hcloud_ssh_key.fingerprint, + "public_key": self.hcloud_ssh_key.public_key, "labels": self.hcloud_ssh_key.labels, } diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/ssh_key_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/ssh_key_info.py index 7a4ab5928..b6b22ff35 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/ssh_key_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/ssh_key_info.py @@ -79,7 +79,6 @@ hcloud_ssh_key_info: """ 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 @@ -92,20 +91,22 @@ class AnsibleHCloudSSHKeyInfo(AnsibleHCloud): hcloud_ssh_key_info: list[BoundSSHKey] | None = None def _prepare_result(self): - ssh_keys = [] + tmp = [] 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 + if ssh_key is None: + continue + + tmp.append( + { + "id": str(ssh_key.id), + "name": ssh_key.name, + "fingerprint": ssh_key.fingerprint, + "public_key": ssh_key.public_key, + "labels": ssh_key.labels, + } + ) + return tmp def get_ssh_keys(self): try: diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/subnetwork.py b/ansible_collections/hetzner/hcloud/plugins/modules/subnetwork.py index aea40bb13..6f7feeaad 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/subnetwork.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/subnetwork.py @@ -123,7 +123,6 @@ hcloud_subnetwork: """ 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 @@ -138,10 +137,10 @@ class AnsibleHCloudSubnetwork(AnsibleHCloud): def _prepare_result(self): return { - "network": to_native(self.hcloud_network.name), - "ip_range": to_native(self.hcloud_subnetwork.ip_range), - "type": to_native(self.hcloud_subnetwork.type), - "network_zone": to_native(self.hcloud_subnetwork.network_zone), + "network": self.hcloud_network.name, + "ip_range": self.hcloud_subnetwork.ip_range, + "type": self.hcloud_subnetwork.type, + "network_zone": self.hcloud_subnetwork.network_zone, "gateway": self.hcloud_subnetwork.gateway, "vswitch_id": self.hcloud_subnetwork.vswitch_id, } @@ -174,7 +173,8 @@ class AnsibleHCloudSubnetwork(AnsibleHCloud): if not self.module.check_mode: try: - self.hcloud_network.add_subnet(subnet=NetworkSubnet(**params)).wait_until_finished() + action = self.hcloud_network.add_subnet(subnet=NetworkSubnet(**params)) + action.wait_until_finished() except HCloudException as exception: self.fail_json_hcloud(exception) @@ -194,7 +194,8 @@ class AnsibleHCloudSubnetwork(AnsibleHCloud): if self.hcloud_subnetwork is not None and self.hcloud_network is not None: if not self.module.check_mode: try: - self.hcloud_network.delete_subnet(self.hcloud_subnetwork).wait_until_finished() + action = self.hcloud_network.delete_subnet(self.hcloud_subnetwork) + action.wait_until_finished() except HCloudException as exception: self.fail_json_hcloud(exception) self._mark_as_changed() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/volume.py b/ansible_collections/hetzner/hcloud/plugins/modules/volume.py index 8442ed90b..1bcb44253 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/volume.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/volume.py @@ -160,7 +160,6 @@ hcloud_volume: """ 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 @@ -173,18 +172,14 @@ class AnsibleHCloudVolume(AnsibleHCloud): hcloud_volume: BoundVolume | None = None def _prepare_result(self): - server_name = None - if self.hcloud_volume.server is not None: - server_name = to_native(self.hcloud_volume.server.name) - return { - "id": to_native(self.hcloud_volume.id), - "name": to_native(self.hcloud_volume.name), + "id": str(self.hcloud_volume.id), + "name": self.hcloud_volume.name, "size": self.hcloud_volume.size, - "location": to_native(self.hcloud_volume.location.name), + "location": self.hcloud_volume.location.name, "labels": self.hcloud_volume.labels, - "server": server_name, - "linux_device": to_native(self.hcloud_volume.linux_device), + "server": self.hcloud_volume.server.name if self.hcloud_volume.server is not None else None, + "linux_device": self.hcloud_volume.linux_device, "delete_protection": self.hcloud_volume.protection["delete"], } @@ -221,7 +216,8 @@ class AnsibleHCloudVolume(AnsibleHCloud): delete_protection = self.module.params.get("delete_protection") if delete_protection is not None: self._get_volume() - self.hcloud_volume.change_protection(delete=delete_protection).wait_until_finished() + action = self.hcloud_volume.change_protection(delete=delete_protection) + action.wait_until_finished() except HCloudException as exception: self.fail_json_hcloud(exception) self._mark_as_changed() @@ -233,7 +229,8 @@ class AnsibleHCloudVolume(AnsibleHCloud): if size: if self.hcloud_volume.size < size: if not self.module.check_mode: - self.hcloud_volume.resize(size).wait_until_finished() + action = self.hcloud_volume.resize(size) + action.wait_until_finished() self._mark_as_changed() elif self.hcloud_volume.size > size: self.module.warn("Shrinking of volumes is not supported") @@ -244,12 +241,14 @@ class AnsibleHCloudVolume(AnsibleHCloud): if self.hcloud_volume.server is None or self.hcloud_volume.server.name != server.name: if not self.module.check_mode: automount = self.module.params.get("automount", False) - self.hcloud_volume.attach(server, automount=automount).wait_until_finished() + action = self.hcloud_volume.attach(server, automount=automount) + action.wait_until_finished() self._mark_as_changed() else: if self.hcloud_volume.server is not None: if not self.module.check_mode: - self.hcloud_volume.detach().wait_until_finished() + action = self.hcloud_volume.detach() + action.wait_until_finished() self._mark_as_changed() labels = self.module.params.get("labels") @@ -261,7 +260,8 @@ class AnsibleHCloudVolume(AnsibleHCloud): delete_protection = self.module.params.get("delete_protection") if delete_protection is not None and delete_protection != self.hcloud_volume.protection["delete"]: if not self.module.check_mode: - self.hcloud_volume.change_protection(delete=delete_protection).wait_until_finished() + action = self.hcloud_volume.change_protection(delete=delete_protection) + action.wait_until_finished() self._mark_as_changed() self._get_volume() @@ -281,7 +281,8 @@ class AnsibleHCloudVolume(AnsibleHCloud): if self.hcloud_volume is not None: if not self.module.check_mode: if self.hcloud_volume.server is not None: - self.hcloud_volume.detach().wait_until_finished() + action = self.hcloud_volume.detach() + action.wait_until_finished() self.client.volumes.delete(self.hcloud_volume) self._mark_as_changed() self.hcloud_volume = None diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/volume_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/volume_info.py index 1e507690e..b30956d74 100644 --- a/ansible_collections/hetzner/hcloud/plugins/modules/volume_info.py +++ b/ansible_collections/hetzner/hcloud/plugins/modules/volume_info.py @@ -95,7 +95,6 @@ hcloud_volume_info: """ 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 @@ -111,22 +110,21 @@ class AnsibleHCloudVolumeInfo(AnsibleHCloud): 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"], - } - ) + if volume is None: + continue + + tmp.append( + { + "id": str(volume.id), + "name": volume.name, + "size": volume.size, + "location": volume.location.name, + "labels": volume.labels, + "server": volume.server.name if volume.server is not None else None, + "linux_device": volume.linux_device, + "delete_protection": volume.protection["delete"], + } + ) return tmp |