From ff539800aae061239edec73524b5868f53154e7b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 8 Aug 2023 18:17:22 +0200 Subject: [PATCH] deps: update dependency hcloud to v1.27.1 (#290) * deps: update dependency hcloud to v1.27.1 * chore: update vendored files --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: jo --- .../module_utils/vendor/hcloud/__init__.py | 2 + plugins/module_utils/vendor/hcloud/_client.py | 65 ++- .../module_utils/vendor/hcloud/_exceptions.py | 7 +- .../module_utils/vendor/hcloud/_version.py | 4 +- .../vendor/hcloud/actions/__init__.py | 9 + .../vendor/hcloud/actions/client.py | 50 +- .../vendor/hcloud/actions/domain.py | 28 +- .../vendor/hcloud/certificates/__init__.py | 13 + .../vendor/hcloud/certificates/client.py | 171 +++--- .../vendor/hcloud/certificates/domain.py | 45 +- .../vendor/hcloud/core/__init__.py | 4 + .../module_utils/vendor/hcloud/core/client.py | 108 ++-- .../module_utils/vendor/hcloud/core/domain.py | 45 +- .../vendor/hcloud/datacenters/__init__.py | 8 + .../vendor/hcloud/datacenters/client.py | 52 +- .../vendor/hcloud/datacenters/domain.py | 24 +- .../vendor/hcloud/deprecation/__init__.py | 3 + .../vendor/hcloud/deprecation/domain.py | 8 +- .../vendor/hcloud/firewalls/__init__.py | 10 + .../vendor/hcloud/firewalls/client.py | 224 +++++--- .../vendor/hcloud/firewalls/domain.py | 63 ++- .../vendor/hcloud/floating_ips/__init__.py | 8 + .../vendor/hcloud/floating_ips/client.py | 213 ++++---- .../vendor/hcloud/floating_ips/domain.py | 40 +- plugins/module_utils/vendor/hcloud/hcloud.py | 2 + .../vendor/hcloud/helpers/__init__.py | 3 + .../vendor/hcloud/helpers/labels.py | 7 +- .../vendor/hcloud/images/__init__.py | 4 + .../vendor/hcloud/images/client.py | 186 ++++--- .../vendor/hcloud/images/domain.py | 49 +- .../vendor/hcloud/isos/__init__.py | 4 + .../module_utils/vendor/hcloud/isos/client.py | 60 ++- .../module_utils/vendor/hcloud/isos/domain.py | 16 +- .../hcloud/load_balancer_types/__init__.py | 8 + .../hcloud/load_balancer_types/client.py | 51 +- .../hcloud/load_balancer_types/domain.py | 20 +- .../vendor/hcloud/load_balancers/__init__.py | 23 + .../vendor/hcloud/load_balancers/client.py | 493 ++++++++--------- .../vendor/hcloud/load_balancers/domain.py | 213 ++++++-- .../vendor/hcloud/locations/__init__.py | 4 + .../vendor/hcloud/locations/client.py | 45 +- .../vendor/hcloud/locations/domain.py | 20 +- .../vendor/hcloud/networks/__init__.py | 9 + .../vendor/hcloud/networks/client.py | 254 +++++---- .../vendor/hcloud/networks/domain.py | 44 +- .../hcloud/placement_groups/__init__.py | 8 + .../vendor/hcloud/placement_groups/client.py | 108 ++-- .../vendor/hcloud/placement_groups/domain.py | 22 +- .../vendor/hcloud/primary_ips/__init__.py | 4 + .../vendor/hcloud/primary_ips/client.py | 167 +++--- .../vendor/hcloud/primary_ips/domain.py | 41 +- plugins/module_utils/vendor/hcloud/py.typed | 1 + .../vendor/hcloud/server_types/__init__.py | 8 + .../vendor/hcloud/server_types/client.py | 45 +- .../vendor/hcloud/server_types/domain.py | 32 +- .../vendor/hcloud/servers/__init__.py | 16 + .../vendor/hcloud/servers/client.py | 506 ++++++++++-------- .../vendor/hcloud/servers/domain.py | 128 +++-- .../vendor/hcloud/ssh_keys/__init__.py | 4 + .../vendor/hcloud/ssh_keys/client.py | 104 ++-- .../vendor/hcloud/ssh_keys/domain.py | 16 +- .../vendor/hcloud/volumes/__init__.py | 4 + .../vendor/hcloud/volumes/client.py | 198 ++++--- .../vendor/hcloud/volumes/domain.py | 40 +- scripts/vendor.py | 2 +- 65 files changed, 2489 insertions(+), 1684 deletions(-) create mode 100644 plugins/module_utils/vendor/hcloud/py.typed diff --git a/plugins/module_utils/vendor/hcloud/__init__.py b/plugins/module_utils/vendor/hcloud/__init__.py index 5beda91..544a219 100644 --- a/plugins/module_utils/vendor/hcloud/__init__.py +++ b/plugins/module_utils/vendor/hcloud/__init__.py @@ -1,2 +1,4 @@ +from __future__ import annotations + from ._client import Client # noqa from ._exceptions import APIException, HCloudException # noqa diff --git a/plugins/module_utils/vendor/hcloud/_client.py b/plugins/module_utils/vendor/hcloud/_client.py index 5dc4f1e..0875daf 100644 --- a/plugins/module_utils/vendor/hcloud/_client.py +++ b/plugins/module_utils/vendor/hcloud/_client.py @@ -1,5 +1,7 @@ +from __future__ import annotations + import time -from typing import Optional, Union +from typing import NoReturn try: import requests @@ -8,23 +10,23 @@ except ImportError: from ._version import VERSION from ._exceptions import APIException -from .actions.client import ActionsClient -from .certificates.client import CertificatesClient -from .datacenters.client import DatacentersClient -from .firewalls.client import FirewallsClient -from .floating_ips.client import FloatingIPsClient -from .images.client import ImagesClient -from .isos.client import IsosClient -from .load_balancer_types.client import LoadBalancerTypesClient -from .load_balancers.client import LoadBalancersClient -from .locations.client import LocationsClient -from .networks.client import NetworksClient -from .placement_groups.client import PlacementGroupsClient -from .primary_ips.client import PrimaryIPsClient -from .server_types.client import ServerTypesClient -from .servers.client import ServersClient -from .ssh_keys.client import SSHKeysClient -from .volumes.client import VolumesClient +from .actions import ActionsClient +from .certificates import CertificatesClient +from .datacenters import DatacentersClient +from .firewalls import FirewallsClient +from .floating_ips import FloatingIPsClient +from .images import ImagesClient +from .isos import IsosClient +from .load_balancer_types import LoadBalancerTypesClient +from .load_balancers import LoadBalancersClient +from .locations import LocationsClient +from .networks import NetworksClient +from .placement_groups import PlacementGroupsClient +from .primary_ips import PrimaryIPsClient +from .server_types import ServerTypesClient +from .servers import ServersClient +from .ssh_keys import SSHKeysClient +from .volumes import VolumesClient class Client: @@ -38,9 +40,10 @@ class Client: self, token: str, api_endpoint: str = "https://api.hetzner.cloud/v1", - application_name: Optional[str] = None, - application_version: Optional[str] = None, + application_name: str | None = None, + application_version: str | None = None, poll_interval: int = 1, + timeout: float | tuple[float, float] | None = None, ): """Create an new Client instance @@ -49,12 +52,14 @@ class Client: :param application_name: Your application name :param application_version: Your application _version :param poll_interval: Interval for polling information from Hetzner Cloud API in seconds + :param timeout: Requests timeout in seconds """ self.token = token self._api_endpoint = api_endpoint self._application_name = application_name self._application_version = application_version self._requests_session = requests.Session() + self._requests_timeout = timeout self.poll_interval = poll_interval self.datacenters = DatacentersClient(self) @@ -169,38 +174,42 @@ class Client: } return headers - def _raise_exception_from_response(self, response): + def _raise_exception_from_response(self, response) -> NoReturn: raise APIException( code=response.status_code, message=response.reason, details={"content": response.content}, ) - def _raise_exception_from_content(self, content: dict): + def _raise_exception_from_content(self, content: dict) -> NoReturn: raise APIException( code=content["error"]["code"], message=content["error"]["message"], details=content["error"]["details"], ) - def request( + def request( # type: ignore[no-untyped-def] self, method: str, url: str, tries: int = 1, **kwargs, - ) -> Union[bytes, dict]: + ) -> dict: """Perform a request to the Hetzner Cloud API, wrapper around requests.request :param method: HTTP Method to perform the Request :param url: URL of the Endpoint :param tries: Tries of the request (used internally, should not be set by the user) + :param timeout: Requests timeout in seconds :return: Response """ + timeout = kwargs.pop("timeout", self._requests_timeout) + response = self._requests_session.request( method=method, url=self._api_endpoint + url, headers=self._get_headers(), + timeout=timeout, **kwargs, ) @@ -213,13 +222,15 @@ class Client: if not response.ok: if content: + assert isinstance(content, dict) if content["error"]["code"] == "rate_limit_exceeded" and tries < 5: time.sleep(tries * self._retry_wait_time) tries = tries + 1 return self.request(method, url, tries, **kwargs) - else: - self._raise_exception_from_content(content) + + self._raise_exception_from_content(content) else: self._raise_exception_from_response(response) - return content + # TODO: return an empty dict instead of an empty string when content == "". + return content # type: ignore[return-value] diff --git a/plugins/module_utils/vendor/hcloud/_exceptions.py b/plugins/module_utils/vendor/hcloud/_exceptions.py index d0801e9..51e3745 100644 --- a/plugins/module_utils/vendor/hcloud/_exceptions.py +++ b/plugins/module_utils/vendor/hcloud/_exceptions.py @@ -1,3 +1,8 @@ +from __future__ import annotations + +from typing import Any + + class HCloudException(Exception): """There was an error while using the hcloud library""" @@ -5,7 +10,7 @@ class HCloudException(Exception): class APIException(HCloudException): """There was an error while performing an API Request""" - def __init__(self, code, message, details): + def __init__(self, code: int | str, message: str, details: Any): super().__init__(message) self.code = code self.message = message diff --git a/plugins/module_utils/vendor/hcloud/_version.py b/plugins/module_utils/vendor/hcloud/_version.py index 974595a..29fb2a1 100644 --- a/plugins/module_utils/vendor/hcloud/_version.py +++ b/plugins/module_utils/vendor/hcloud/_version.py @@ -1 +1,3 @@ -VERSION = "1.26.0" # x-release-please-version +from __future__ import annotations + +VERSION = "1.27.1" # x-release-please-version diff --git a/plugins/module_utils/vendor/hcloud/actions/__init__.py b/plugins/module_utils/vendor/hcloud/actions/__init__.py index e69de29..66bb7b4 100644 --- a/plugins/module_utils/vendor/hcloud/actions/__init__.py +++ b/plugins/module_utils/vendor/hcloud/actions/__init__.py @@ -0,0 +1,9 @@ +from __future__ import annotations + +from .client import ActionsClient, ActionsPageResult, BoundAction # noqa: F401 +from .domain import ( # noqa: F401 + Action, + ActionException, + ActionFailedException, + ActionTimeoutException, +) diff --git a/plugins/module_utils/vendor/hcloud/actions/client.py b/plugins/module_utils/vendor/hcloud/actions/client.py index 2186b65..3105b3c 100644 --- a/plugins/module_utils/vendor/hcloud/actions/client.py +++ b/plugins/module_utils/vendor/hcloud/actions/client.py @@ -1,13 +1,21 @@ -import time +from __future__ import annotations -from ..core.client import BoundModelBase, ClientEntityBase +import time +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..core import BoundModelBase, ClientEntityBase, Meta from .domain import Action, ActionFailedException, ActionTimeoutException +if TYPE_CHECKING: + from .._client import Client + class BoundAction(BoundModelBase): + _client: ActionsClient + model = Action - def wait_until_finished(self, max_retries=100): + def wait_until_finished(self, max_retries: int = 100) -> None: """Wait until the specific action has status="finished" (set Client.poll_interval to specify a delay between checks) :param max_retries: int @@ -27,11 +35,15 @@ class BoundAction(BoundModelBase): raise ActionFailedException(action=self) -class ActionsClient(ClientEntityBase): - results_list_attribute_name = "actions" +class ActionsPageResult(NamedTuple): + actions: list[BoundAction] + meta: Meta | None - def get_by_id(self, id): - # type: (int) -> BoundAction + +class ActionsClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundAction: """Get a specific action by its ID. :param id: int @@ -43,12 +55,11 @@ class ActionsClient(ClientEntityBase): def get_list( self, - status=None, # type: Optional[List[str]] - sort=None, # type: Optional[List[str]] - page=None, # type: Optional[int] - per_page=None, # type: Optional[int] - ): - # type: (...) -> PageResults[List[BoundAction]] + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: """Get a list of actions from this account :param status: List[str] (optional) @@ -61,7 +72,7 @@ class ActionsClient(ClientEntityBase): Specifies how many results are returned by page :return: (List[:class:`BoundAction `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if status is not None: params["status"] = status if sort is not None: @@ -75,10 +86,13 @@ class ActionsClient(ClientEntityBase): actions = [ BoundAction(self, action_data) for action_data in response["actions"] ] - return self._add_meta_to_result(actions, response) + return ActionsPageResult(actions, Meta.parse_meta(response)) - def get_all(self, status=None, sort=None): - # type: (Optional[List[str]], Optional[List[str]]) -> List[BoundAction] + def get_all( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: """Get all actions of the account :param status: List[str] (optional) @@ -87,4 +101,4 @@ class ActionsClient(ClientEntityBase): Specify how the results are sorted. Choices: `id` `command` `status` `progress` `started` `finished` . You can add one of ":asc", ":desc" to modify sort order. ( ":asc" is default) :return: List[:class:`BoundAction `] """ - return super().get_all(status=status, sort=sort) + return self._iter_pages(self.get_list, status=status, sort=sort) diff --git a/plugins/module_utils/vendor/hcloud/actions/domain.py b/plugins/module_utils/vendor/hcloud/actions/domain.py index 4691472..16b74ac 100644 --- a/plugins/module_utils/vendor/hcloud/actions/domain.py +++ b/plugins/module_utils/vendor/hcloud/actions/domain.py @@ -1,10 +1,17 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + try: from dateutil.parser import isoparse except ImportError: isoparse = None from .._exceptions import HCloudException -from ..core.domain import BaseDomain +from ..core import BaseDomain + +if TYPE_CHECKING: + from .client import BoundAction class Action(BaseDomain): @@ -40,14 +47,14 @@ class Action(BaseDomain): def __init__( self, - id, - command=None, - status=None, - progress=None, - started=None, - finished=None, - resources=None, - error=None, + id: int, + command: str | None = None, + status: str | None = None, + progress: int | None = None, + started: str | None = None, + finished: str | None = None, + resources: list[dict] | None = None, + error: dict | None = None, ): self.id = id self.command = command @@ -63,7 +70,8 @@ class Action(BaseDomain): class ActionException(HCloudException): """A generic action exception""" - def __init__(self, action): + def __init__(self, action: Action | BoundAction): + assert self.__doc__ is not None message = self.__doc__ if action.error is not None and "message" in action.error: message += f": {action.error['message']}" diff --git a/plugins/module_utils/vendor/hcloud/certificates/__init__.py b/plugins/module_utils/vendor/hcloud/certificates/__init__.py index e69de29..4e63df5 100644 --- a/plugins/module_utils/vendor/hcloud/certificates/__init__.py +++ b/plugins/module_utils/vendor/hcloud/certificates/__init__.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +from .client import ( # noqa: F401 + BoundCertificate, + CertificatesClient, + CertificatesPageResult, +) +from .domain import ( # noqa: F401 + Certificate, + CreateManagedCertificateResponse, + ManagedCertificateError, + ManagedCertificateStatus, +) diff --git a/plugins/module_utils/vendor/hcloud/certificates/client.py b/plugins/module_utils/vendor/hcloud/certificates/client.py index 1ab18bd..1067dfa 100644 --- a/plugins/module_utils/vendor/hcloud/certificates/client.py +++ b/plugins/module_utils/vendor/hcloud/certificates/client.py @@ -1,6 +1,9 @@ -from ..actions.client import BoundAction -from ..core.client import BoundModelBase, ClientEntityBase, GetEntityByNameMixin -from ..core.domain import add_meta_to_result +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..actions import ActionsPageResult, BoundAction +from ..core import BoundModelBase, ClientEntityBase, Meta from .domain import ( Certificate, CreateManagedCertificateResponse, @@ -8,11 +11,16 @@ from .domain import ( ManagedCertificateStatus, ) +if TYPE_CHECKING: + from .._client import Client + class BoundCertificate(BoundModelBase): + _client: CertificatesClient + model = Certificate - def __init__(self, client, data, complete=True): + def __init__(self, client: CertificatesClient, data: dict, complete: bool = True): status = data.get("status") if status is not None: error_data = status.get("error") @@ -26,8 +34,13 @@ class BoundCertificate(BoundModelBase): ) super().__init__(client, data, complete) - def get_actions_list(self, status=None, sort=None, page=None, per_page=None): - # type: (Optional[List[str]], Optional[List[str]], Optional[int], Optional[int]) -> PageResults[List[BoundAction, Meta]] + def get_actions_list( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: """Returns all action objects for a Certificate. :param status: List[str] (optional) @@ -42,8 +55,11 @@ class BoundCertificate(BoundModelBase): """ return self._client.get_actions_list(self, status, sort, page, per_page) - def get_actions(self, status=None, sort=None): - # type: (Optional[List[str]], Optional[List[str]]) -> List[BoundAction] + def get_actions( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: """Returns all action objects for a Certificate. :param status: List[str] (optional) @@ -54,8 +70,11 @@ class BoundCertificate(BoundModelBase): """ return self._client.get_actions(self, status, sort) - def update(self, name=None, labels=None): - # type: (Optional[str], Optional[Dict[str, str]]) -> BoundCertificate + def update( + self, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundCertificate: """Updates an certificate. You can update an certificate name and the certificate labels. :param name: str (optional) @@ -66,26 +85,28 @@ class BoundCertificate(BoundModelBase): """ return self._client.update(self, name, labels) - def delete(self): - # type: () -> bool + def delete(self) -> bool: """Deletes a certificate. :return: boolean """ return self._client.delete(self) - def retry_issuance(self): - # type: () -> BoundAction + def retry_issuance(self) -> BoundAction: """Retry a failed Certificate issuance or renewal. :return: BoundAction """ return self._client.retry_issuance(self) -class CertificatesClient(ClientEntityBase, GetEntityByNameMixin): - results_list_attribute_name = "certificates" +class CertificatesPageResult(NamedTuple): + certificates: list[BoundCertificate] + meta: Meta | None - def get_by_id(self, id): - # type: (int) -> BoundCertificate + +class CertificatesClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundCertificate: """Get a specific certificate by its ID. :param id: int @@ -96,12 +117,11 @@ class CertificatesClient(ClientEntityBase, GetEntityByNameMixin): def get_list( self, - name=None, # type: Optional[str] - label_selector=None, # type: Optional[str] - page=None, # type: Optional[int] - per_page=None, # type: Optional[int] - ): - # type: (...) -> PageResults[List[BoundCertificate], Meta] + name: str | None = None, + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> CertificatesPageResult: """Get a list of certificates :param name: str (optional) @@ -114,7 +134,7 @@ class CertificatesClient(ClientEntityBase, GetEntityByNameMixin): Specifies how many results are returned by page :return: (List[:class:`BoundCertificate `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if name is not None: params["name"] = name @@ -136,10 +156,13 @@ class CertificatesClient(ClientEntityBase, GetEntityByNameMixin): for certificate_data in response["certificates"] ] - return self._add_meta_to_result(certificates, response) + return CertificatesPageResult(certificates, Meta.parse_meta(response)) - def get_all(self, name=None, label_selector=None): - # type: (Optional[str], Optional[str]) -> List[BoundCertificate] + def get_all( + self, + name: str | None = None, + label_selector: str | None = None, + ) -> list[BoundCertificate]: """Get all certificates :param name: str (optional) @@ -148,20 +171,24 @@ class CertificatesClient(ClientEntityBase, GetEntityByNameMixin): Can be used to filter certificates by labels. The response will only contain certificates matching the label selector. :return: List[:class:`BoundCertificate `] """ - return super().get_all(name=name, label_selector=label_selector) + return self._iter_pages(self.get_list, name=name, label_selector=label_selector) - def get_by_name(self, name): - # type: (str) -> BoundCertificate + def get_by_name(self, name: str) -> BoundCertificate | None: """Get certificate by name :param name: str Used to get certificate by name. :return: :class:`BoundCertificate ` """ - return super().get_by_name(name) + return self._get_first_by(name=name) - def create(self, name, certificate, private_key, labels=None): - # type: (str, str, str, Optional[Dict[str, str]]) -> BoundCertificate + def create( + self, + name: str, + certificate: str, + private_key: str, + labels: dict[str, str] | None = None, + ) -> BoundCertificate: """Creates a new Certificate with the given name, certificate and private_key. This methods allows only creating custom uploaded certificates. If you want to create a managed certificate use :func:`~hcloud.certificates.client.CertificatesClient.create_managed` @@ -174,7 +201,7 @@ class CertificatesClient(ClientEntityBase, GetEntityByNameMixin): User-defined labels (key-value pairs) :return: :class:`BoundCertificate ` """ - data = { + data: dict[str, Any] = { "name": name, "certificate": certificate, "private_key": private_key, @@ -185,8 +212,12 @@ class CertificatesClient(ClientEntityBase, GetEntityByNameMixin): response = self._client.request(url="/certificates", method="POST", json=data) return BoundCertificate(self, response["certificate"]) - def create_managed(self, name, domain_names, labels=None): - # type: (str, List[str], Optional[Dict[str, str]]) -> CreateManagedCertificateResponse + def create_managed( + self, + name: str, + domain_names: list[str], + labels: dict[str, str] | None = None, + ) -> CreateManagedCertificateResponse: """Creates a new managed Certificate with the given name and domain names. This methods allows only creating managed certificates for domains that are using the Hetzner DNS service. If you want to create a custom uploaded certificate use :func:`~hcloud.certificates.client.CertificatesClient.create` @@ -197,7 +228,7 @@ class CertificatesClient(ClientEntityBase, GetEntityByNameMixin): User-defined labels (key-value pairs) :return: :class:`BoundCertificate ` """ - data = { + data: dict[str, Any] = { "name": name, "type": Certificate.TYPE_MANAGED, "domain_names": domain_names, @@ -210,8 +241,12 @@ class CertificatesClient(ClientEntityBase, GetEntityByNameMixin): action=BoundAction(self._client.actions, response["action"]), ) - def update(self, certificate, name=None, labels=None): - # type: (Certificate, Optional[str], Optional[Dict[str, str]]) -> BoundCertificate + def update( + self, + certificate: Certificate | BoundCertificate, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundCertificate: """Updates a Certificate. You can update a certificate name and labels. :param certificate: :class:`BoundCertificate ` or :class:`Certificate ` @@ -221,7 +256,7 @@ class CertificatesClient(ClientEntityBase, GetEntityByNameMixin): User-defined labels (key-value pairs) :return: :class:`BoundCertificate ` """ - data = {} + data: dict[str, Any] = {} if name is not None: data["name"] = name if labels is not None: @@ -233,24 +268,27 @@ class CertificatesClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundCertificate(self, response["certificate"]) - def delete(self, certificate): - # type: (Certificate) -> bool - self._client.request( - url=f"/certificates/{certificate.id}", - method="DELETE", - ) + def delete(self, certificate: Certificate | BoundCertificate) -> bool: """Deletes a certificate. :param certificate: :class:`BoundCertificate ` or :class:`Certificate ` :return: True """ + self._client.request( + url=f"/certificates/{certificate.id}", + method="DELETE", + ) # Return always true, because the API does not return an action for it. When an error occurs a HcloudAPIException will be raised return True def get_actions_list( - self, certificate, status=None, sort=None, page=None, per_page=None - ): - # type: (Certificate, Optional[List[str]], Optional[List[str]], Optional[int], Optional[int]) -> PageResults[List[BoundAction], Meta] + self, + certificate: Certificate | BoundCertificate, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: """Returns all action objects for a Certificate. :param certificate: :class:`BoundCertificate ` or :class:`Certificate ` @@ -264,7 +302,7 @@ class CertificatesClient(ClientEntityBase, GetEntityByNameMixin): Specifies how many results are returned by page :return: (List[:class:`BoundAction `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if status is not None: params["status"] = status if sort is not None: @@ -275,9 +313,7 @@ class CertificatesClient(ClientEntityBase, GetEntityByNameMixin): params["per_page"] = per_page response = self._client.request( - url="/certificates/{certificate_id}/actions".format( - certificate_id=certificate.id - ), + url=f"/certificates/{certificate.id}/actions", method="GET", params=params, ) @@ -285,10 +321,14 @@ class CertificatesClient(ClientEntityBase, GetEntityByNameMixin): BoundAction(self._client.actions, action_data) for action_data in response["actions"] ] - return add_meta_to_result(actions, response, "actions") + return ActionsPageResult(actions, Meta.parse_meta(response)) - def get_actions(self, certificate, status=None, sort=None): - # type: (Certificate, Optional[List[str]], Optional[List[str]]) -> List[BoundAction] + def get_actions( + self, + certificate: Certificate | BoundCertificate, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: """Returns all action objects for a Certificate. :param certificate: :class:`BoundCertificate ` or :class:`Certificate ` @@ -298,19 +338,24 @@ class CertificatesClient(ClientEntityBase, GetEntityByNameMixin): Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` :return: List[:class:`BoundAction `] """ - return super().get_actions(certificate, status=status, sort=sort) + return self._iter_pages( + self.get_actions_list, + certificate, + status=status, + sort=sort, + ) - def retry_issuance(self, certificate): - # type: (Certificate) -> BoundAction + def retry_issuance( + self, + certificate: Certificate | BoundCertificate, + ) -> BoundAction: """Returns all action objects for a Certificate. :param certificate: :class:`BoundCertificate ` or :class:`Certificate ` :return: :class:`BoundAction ` """ response = self._client.request( - url="/certificates/{certificate_id}/actions/retry".format( - certificate_id=certificate.id - ), + url=f"/certificates/{certificate.id}/actions/retry", method="POST", ) return BoundAction(self._client.actions, response["action"]) diff --git a/plugins/module_utils/vendor/hcloud/certificates/domain.py b/plugins/module_utils/vendor/hcloud/certificates/domain.py index de03f52..c09c288 100644 --- a/plugins/module_utils/vendor/hcloud/certificates/domain.py +++ b/plugins/module_utils/vendor/hcloud/certificates/domain.py @@ -1,9 +1,17 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + try: from dateutil.parser import isoparse except ImportError: isoparse = None -from ..core.domain import BaseDomain, DomainIdentityMixin +from ..core import BaseDomain, DomainIdentityMixin + +if TYPE_CHECKING: + from ..actions import BoundAction + from .client import BoundCertificate class Certificate(BaseDomain, DomainIdentityMixin): @@ -44,17 +52,17 @@ class Certificate(BaseDomain, DomainIdentityMixin): def __init__( self, - id=None, - name=None, - certificate=None, - not_valid_before=None, - not_valid_after=None, - domain_names=None, - fingerprint=None, - created=None, - labels=None, - type=None, - status=None, + id: int | None = None, + name: str | None = None, + certificate: str | None = None, + not_valid_before: str | None = None, + not_valid_after: str | None = None, + domain_names: list[str] | None = None, + fingerprint: str | None = None, + created: str | None = None, + labels: dict[str, str] | None = None, + type: str | None = None, + status: ManagedCertificateStatus | None = None, ): self.id = id self.name = name @@ -80,7 +88,12 @@ class ManagedCertificateStatus(BaseDomain): If issuance or renewal reports failure, this property contains information about what happened """ - def __init__(self, issuance=None, renewal=None, error=None): + def __init__( + self, + issuance: str | None = None, + renewal: str | None = None, + error: ManagedCertificateError | None = None, + ): self.issuance = issuance self.renewal = renewal self.error = error @@ -95,7 +108,7 @@ class ManagedCertificateError(BaseDomain): Message detailing the error """ - def __init__(self, code=None, message=None): + def __init__(self, code: str | None = None, message: str | None = None): self.code = code self.message = message @@ -113,8 +126,8 @@ class CreateManagedCertificateResponse(BaseDomain): def __init__( self, - certificate, # type: BoundCertificate - action, # type: BoundAction + certificate: BoundCertificate, + action: BoundAction, ): self.certificate = certificate self.action = action diff --git a/plugins/module_utils/vendor/hcloud/core/__init__.py b/plugins/module_utils/vendor/hcloud/core/__init__.py index e69de29..4e17dac 100644 --- a/plugins/module_utils/vendor/hcloud/core/__init__.py +++ b/plugins/module_utils/vendor/hcloud/core/__init__.py @@ -0,0 +1,4 @@ +from __future__ import annotations + +from .client import BoundModelBase, ClientEntityBase # noqa: F401 +from .domain import BaseDomain, DomainIdentityMixin, Meta, Pagination # noqa: F401 diff --git a/plugins/module_utils/vendor/hcloud/core/client.py b/plugins/module_utils/vendor/hcloud/core/client.py index 10a2115..16c37e5 100644 --- a/plugins/module_utils/vendor/hcloud/core/client.py +++ b/plugins/module_utils/vendor/hcloud/core/client.py @@ -1,102 +1,65 @@ -from .domain import add_meta_to_result +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Callable + +if TYPE_CHECKING: + from .._client import Client class ClientEntityBase: - max_per_page = 50 - results_list_attribute_name = None + _client: Client - def __init__(self, client): + max_per_page: int = 50 + + def __init__(self, client: Client): """ :param client: Client :return self """ self._client = client - def _is_list_attribute_implemented(self): - if self.results_list_attribute_name is None: - raise NotImplementedError( - "in order to get results list, 'results_list_attribute_name' attribute of {} has to be specified".format( - self.__class__.__name__ - ) - ) - - def _add_meta_to_result( + def _iter_pages( # type: ignore[no-untyped-def] self, - results, # type: List[BoundModelBase] - response, # type: json - ): - # type: (...) -> PageResult - self._is_list_attribute_implemented() - return add_meta_to_result(results, response, self.results_list_attribute_name) - - def _get_all( - self, - list_function, # type: function - results_list_attribute_name, # type: str + list_function: Callable, *args, - **kwargs - ): - # type (...) -> List[BoundModelBase] - - page = 1 - + **kwargs, + ) -> list: results = [] + page = 1 while page: - page_result = list_function( - page=page, per_page=self.max_per_page, *args, **kwargs + # The *PageResult tuples MUST have the following structure + # `(result: List[Bound*], meta: Meta)` + result, meta = list_function( + *args, page=page, per_page=self.max_per_page, **kwargs ) - result = getattr(page_result, results_list_attribute_name) if result: results.extend(result) - meta = page_result.meta - if ( - meta - and meta.pagination - and meta.pagination.next_page - and meta.pagination.next_page - ): + + if meta and meta.pagination and meta.pagination.next_page: page = meta.pagination.next_page else: - page = None + page = 0 return results - def get_all(self, *args, **kwargs): - # type: (...) -> List[BoundModelBase] - self._is_list_attribute_implemented() - return self._get_all( - self.get_list, self.results_list_attribute_name, *args, **kwargs - ) - - def get_actions(self, *args, **kwargs): - # type: (...) -> List[BoundModelBase] - if not hasattr(self, "get_actions_list"): - raise ValueError("this endpoint does not support get_actions method") - - return self._get_all(self.get_actions_list, "actions", *args, **kwargs) - - -class GetEntityByNameMixin: - """ - Use as a mixin for ClientEntityBase classes - """ - - def get_by_name(self, name): - # type: (str) -> BoundModelBase - self._is_list_attribute_implemented() - response = self.get_list(name=name) - entities = getattr(response, self.results_list_attribute_name) - entity = entities[0] if entities else None - return entity + def _get_first_by(self, **kwargs): # type: ignore[no-untyped-def] + assert hasattr(self, "get_list") + entities, _ = self.get_list(**kwargs) + return entities[0] if entities else None class BoundModelBase: """Bound Model Base""" - model = None + model: Any - def __init__(self, client, data={}, complete=True): + def __init__( + self, + client: ClientEntityBase, + data: dict, + complete: bool = True, + ): """ :param client: The client for the specific model to use @@ -109,7 +72,7 @@ class BoundModelBase: self.complete = complete self.data_model = self.model.from_dict(data) - def __getattr__(self, name): + def __getattr__(self, name: str): # type: ignore[no-untyped-def] """Allow magical access to the properties of the model :param name: str :return: @@ -120,8 +83,9 @@ class BoundModelBase: value = getattr(self.data_model, name) return value - def reload(self): + def reload(self) -> None: """Reloads the model and tries to get all data from the APIx""" + assert hasattr(self._client, "get_by_id") bound_model = self._client.get_by_id(self.data_model.id) self.data_model = bound_model.data_model self.complete = True diff --git a/plugins/module_utils/vendor/hcloud/core/domain.py b/plugins/module_utils/vendor/hcloud/core/domain.py index c4d3308..21aed34 100644 --- a/plugins/module_utils/vendor/hcloud/core/domain.py +++ b/plugins/module_utils/vendor/hcloud/core/domain.py @@ -1,24 +1,27 @@ -from collections import namedtuple +from __future__ import annotations class BaseDomain: __slots__ = () @classmethod - def from_dict(cls, data): + def from_dict(cls, data: dict): # type: ignore[no-untyped-def] supported_data = {k: v for k, v in data.items() if k in cls.__slots__} return cls(**supported_data) def __repr__(self) -> str: - kwargs = [f"{key}={getattr(self, key)!r}" for key in self.__slots__] + kwargs = [f"{key}={getattr(self, key)!r}" for key in self.__slots__] # type: ignore[var-annotated] return f"{self.__class__.__qualname__}({', '.join(kwargs)})" class DomainIdentityMixin: __slots__ = () + id: int | None + name: str | None + @property - def id_or_name(self): + def id_or_name(self) -> int | str: if self.id is not None: return self.id elif self.name is not None: @@ -39,12 +42,12 @@ class Pagination(BaseDomain): def __init__( self, - page, - per_page, - previous_page=None, - next_page=None, - last_page=None, - total_entries=None, + page: int, + per_page: int, + previous_page: int | None = None, + next_page: int | None = None, + last_page: int | None = None, + total_entries: int | None = None, ): self.page = page self.per_page = per_page @@ -57,23 +60,17 @@ class Pagination(BaseDomain): class Meta(BaseDomain): __slots__ = ("pagination",) - def __init__(self, pagination=None): + def __init__(self, pagination: Pagination | None = None): self.pagination = pagination @classmethod - def parse_meta(cls, json_content): + def parse_meta(cls, response: dict) -> Meta | None: meta = None - if json_content and "meta" in json_content: + if response and "meta" in response: meta = cls() - pagination_json = json_content["meta"].get("pagination") - if pagination_json: - pagination = Pagination(**pagination_json) - meta.pagination = pagination + try: + meta.pagination = Pagination(**response["meta"]["pagination"]) + except KeyError: + pass + return meta - - -def add_meta_to_result(result, json_content, attr_name): - # type: (List[BoundModelBase], json, string) -> PageResult - class_name = f"PageResults{attr_name.capitalize()}" - PageResults = namedtuple(class_name, [attr_name, "meta"]) - return PageResults(**{attr_name: result, "meta": Meta.parse_meta(json_content)}) diff --git a/plugins/module_utils/vendor/hcloud/datacenters/__init__.py b/plugins/module_utils/vendor/hcloud/datacenters/__init__.py index e69de29..559694c 100644 --- a/plugins/module_utils/vendor/hcloud/datacenters/__init__.py +++ b/plugins/module_utils/vendor/hcloud/datacenters/__init__.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .client import ( # noqa: F401 + BoundDatacenter, + DatacentersClient, + DatacentersPageResult, +) +from .domain import Datacenter, DatacenterServerTypes # noqa: F401 diff --git a/plugins/module_utils/vendor/hcloud/datacenters/client.py b/plugins/module_utils/vendor/hcloud/datacenters/client.py index 0ef212b..ab5aa5e 100644 --- a/plugins/module_utils/vendor/hcloud/datacenters/client.py +++ b/plugins/module_utils/vendor/hcloud/datacenters/client.py @@ -1,13 +1,22 @@ -from ..core.client import BoundModelBase, ClientEntityBase, GetEntityByNameMixin -from ..locations.client import BoundLocation -from ..server_types.client import BoundServerType +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..core import BoundModelBase, ClientEntityBase, Meta +from ..locations import BoundLocation +from ..server_types import BoundServerType from .domain import Datacenter, DatacenterServerTypes +if TYPE_CHECKING: + from .._client import Client + class BoundDatacenter(BoundModelBase): + _client: DatacentersClient + model = Datacenter - def __init__(self, client, data): + def __init__(self, client: DatacentersClient, data: dict): location = data.get("location") if location is not None: data["location"] = BoundLocation(client._client.locations, location) @@ -41,11 +50,15 @@ class BoundDatacenter(BoundModelBase): super().__init__(client, data) -class DatacentersClient(ClientEntityBase, GetEntityByNameMixin): - results_list_attribute_name = "datacenters" +class DatacentersPageResult(NamedTuple): + datacenters: list[BoundDatacenter] + meta: Meta | None - def get_by_id(self, id): - # type: (int) -> BoundDatacenter + +class DatacentersClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundDatacenter: """Get a specific datacenter by its ID. :param id: int @@ -56,11 +69,10 @@ class DatacentersClient(ClientEntityBase, GetEntityByNameMixin): def get_list( self, - name=None, # type: Optional[str] - page=None, # type: Optional[int] - per_page=None, # type: Optional[int] - ): - # type: (...) -> PageResults[List[BoundDatacenter], Meta] + name: str | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> DatacentersPageResult: """Get a list of datacenters :param name: str (optional) @@ -71,7 +83,7 @@ class DatacentersClient(ClientEntityBase, GetEntityByNameMixin): Specifies how many results are returned by page :return: (List[:class:`BoundDatacenter `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if name is not None: params["name"] = name @@ -88,24 +100,22 @@ class DatacentersClient(ClientEntityBase, GetEntityByNameMixin): for datacenter_data in response["datacenters"] ] - return self._add_meta_to_result(datacenters, response) + return DatacentersPageResult(datacenters, Meta.parse_meta(response)) - def get_all(self, name=None): - # type: (Optional[str]) -> List[BoundDatacenter] + def get_all(self, name: str | None = None) -> list[BoundDatacenter]: """Get all datacenters :param name: str (optional) Can be used to filter datacenters by their name. :return: List[:class:`BoundDatacenter `] """ - return super().get_all(name=name) + return self._iter_pages(self.get_list, name=name) - def get_by_name(self, name): - # type: (str) -> BoundDatacenter + def get_by_name(self, name: str) -> BoundDatacenter | None: """Get datacenter by name :param name: str Used to get datacenter by name. :return: :class:`BoundDatacenter ` """ - return super().get_by_name(name) + return self._get_first_by(name=name) diff --git a/plugins/module_utils/vendor/hcloud/datacenters/domain.py b/plugins/module_utils/vendor/hcloud/datacenters/domain.py index 984cc85..1c59bfa 100644 --- a/plugins/module_utils/vendor/hcloud/datacenters/domain.py +++ b/plugins/module_utils/vendor/hcloud/datacenters/domain.py @@ -1,4 +1,12 @@ -from ..core.domain import BaseDomain, DomainIdentityMixin +from __future__ import annotations + +from typing import TYPE_CHECKING + +from ..core import BaseDomain, DomainIdentityMixin + +if TYPE_CHECKING: + from ..locations import Location + from ..server_types import BoundServerType class Datacenter(BaseDomain, DomainIdentityMixin): @@ -14,7 +22,12 @@ class Datacenter(BaseDomain, DomainIdentityMixin): __slots__ = ("id", "name", "description", "location", "server_types") def __init__( - self, id=None, name=None, description=None, location=None, server_types=None + self, + id: int | None = None, + name: str | None = None, + description: str | None = None, + location: Location | None = None, + server_types: DatacenterServerTypes | None = None, ): self.id = id self.name = name @@ -36,7 +49,12 @@ class DatacenterServerTypes: __slots__ = ("available", "supported", "available_for_migration") - def __init__(self, available, supported, available_for_migration): + def __init__( + self, + available: list[BoundServerType], + supported: list[BoundServerType], + available_for_migration: list[BoundServerType], + ): self.available = available self.supported = supported self.available_for_migration = available_for_migration diff --git a/plugins/module_utils/vendor/hcloud/deprecation/__init__.py b/plugins/module_utils/vendor/hcloud/deprecation/__init__.py index e69de29..315576b 100644 --- a/plugins/module_utils/vendor/hcloud/deprecation/__init__.py +++ b/plugins/module_utils/vendor/hcloud/deprecation/__init__.py @@ -0,0 +1,3 @@ +from __future__ import annotations + +from .domain import DeprecationInfo # noqa: F401 diff --git a/plugins/module_utils/vendor/hcloud/deprecation/domain.py b/plugins/module_utils/vendor/hcloud/deprecation/domain.py index 26d4aa7..b79e709 100644 --- a/plugins/module_utils/vendor/hcloud/deprecation/domain.py +++ b/plugins/module_utils/vendor/hcloud/deprecation/domain.py @@ -1,9 +1,11 @@ +from __future__ import annotations + try: from dateutil.parser import isoparse except ImportError: isoparse = None -from ..core.domain import BaseDomain +from ..core import BaseDomain class DeprecationInfo(BaseDomain): @@ -25,8 +27,8 @@ class DeprecationInfo(BaseDomain): def __init__( self, - announced=None, - unavailable_after=None, + announced: str | None = None, + unavailable_after: str | None = None, ): self.announced = isoparse(announced) if announced else None self.unavailable_after = ( diff --git a/plugins/module_utils/vendor/hcloud/firewalls/__init__.py b/plugins/module_utils/vendor/hcloud/firewalls/__init__.py index e69de29..42bde36 100644 --- a/plugins/module_utils/vendor/hcloud/firewalls/__init__.py +++ b/plugins/module_utils/vendor/hcloud/firewalls/__init__.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +from .client import BoundFirewall, FirewallsClient, FirewallsPageResult # noqa: F401 +from .domain import ( # noqa: F401 + CreateFirewallResponse, + Firewall, + FirewallResource, + FirewallResourceLabelSelector, + FirewallRule, +) diff --git a/plugins/module_utils/vendor/hcloud/firewalls/client.py b/plugins/module_utils/vendor/hcloud/firewalls/client.py index 5ff716d..8696080 100644 --- a/plugins/module_utils/vendor/hcloud/firewalls/client.py +++ b/plugins/module_utils/vendor/hcloud/firewalls/client.py @@ -1,6 +1,9 @@ -from ..actions.client import BoundAction -from ..core.client import BoundModelBase, ClientEntityBase, GetEntityByNameMixin -from ..core.domain import add_meta_to_result +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..actions import ActionsPageResult, BoundAction +from ..core import BoundModelBase, ClientEntityBase, Meta from .domain import ( CreateFirewallResponse, Firewall, @@ -9,11 +12,16 @@ from .domain import ( FirewallRule, ) +if TYPE_CHECKING: + from .._client import Client + class BoundFirewall(BoundModelBase): + _client: FirewallsClient + model = Firewall - def __init__(self, client, data, complete=True): + def __init__(self, client: FirewallsClient, data: dict, complete: bool = True): rules = data.get("rules", []) if rules: rules = [ @@ -31,7 +39,7 @@ class BoundFirewall(BoundModelBase): applied_to = data.get("applied_to", []) if applied_to: - from ..servers.client import BoundServer + from ..servers import BoundServer ats = [] for a in applied_to: @@ -57,8 +65,13 @@ class BoundFirewall(BoundModelBase): super().__init__(client, data, complete) - def get_actions_list(self, status=None, sort=None, page=None, per_page=None): - # type: (Optional[List[str]], Optional[List[str]], Optional[int], Optional[int]) -> PageResult[BoundAction, Meta] + def get_actions_list( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: """Returns all action objects for a Firewall. :param status: List[str] (optional) @@ -73,8 +86,11 @@ class BoundFirewall(BoundModelBase): """ return self._client.get_actions_list(self, status, sort, page, per_page) - def get_actions(self, status=None, sort=None): - # type: (Optional[List[str]], Optional[List[str]]) -> List[BoundAction] + def get_actions( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: """Returns all action objects for a Firewall. :param status: List[str] (optional) @@ -86,8 +102,11 @@ class BoundFirewall(BoundModelBase): """ return self._client.get_actions(self, status, sort) - def update(self, name=None, labels=None): - # type: (Optional[str], Optional[Dict[str, str]], Optional[str]) -> BoundFirewall + def update( + self, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundFirewall: """Updates the name or labels of a Firewall. :param labels: Dict[str, str] (optional) @@ -98,16 +117,14 @@ class BoundFirewall(BoundModelBase): """ return self._client.update(self, labels, name) - def delete(self): - # type: () -> bool + def delete(self) -> bool: """Deletes a Firewall. :return: boolean """ return self._client.delete(self) - def set_rules(self, rules): - # type: (List[FirewallRule]) -> List[BoundAction] + def set_rules(self, rules: list[FirewallRule]) -> list[BoundAction]: """Sets the rules of a Firewall. All existing rules will be overwritten. Pass an empty rules array to remove all rules. :param rules: List[:class:`FirewallRule `] :return: List[:class:`BoundAction `] @@ -115,16 +132,20 @@ class BoundFirewall(BoundModelBase): return self._client.set_rules(self, rules) - def apply_to_resources(self, resources): - # type: (List[FirewallResource]) -> List[BoundAction] + def apply_to_resources( + self, + resources: list[FirewallResource], + ) -> list[BoundAction]: """Applies one Firewall to multiple resources. :param resources: List[:class:`FirewallResource `] :return: List[:class:`BoundAction `] """ return self._client.apply_to_resources(self, resources) - def remove_from_resources(self, resources): - # type: (List[FirewallResource]) -> List[BoundAction] + def remove_from_resources( + self, + resources: list[FirewallResource], + ) -> list[BoundAction]: """Removes one Firewall from multiple resources. :param resources: List[:class:`FirewallResource `] :return: List[:class:`BoundAction `] @@ -132,18 +153,22 @@ class BoundFirewall(BoundModelBase): return self._client.remove_from_resources(self, resources) -class FirewallsClient(ClientEntityBase, GetEntityByNameMixin): - results_list_attribute_name = "firewalls" +class FirewallsPageResult(NamedTuple): + firewalls: list[BoundFirewall] + meta: Meta | None + + +class FirewallsClient(ClientEntityBase): + _client: Client def get_actions_list( self, - firewall, # type: Firewall - status=None, # type: Optional[List[str]] - sort=None, # type: Optional[List[str]] - page=None, # type: Optional[int] - per_page=None, # type: Optional[int] - ): - # type: (...) -> PageResults[List[BoundAction], Meta] + firewall: Firewall | BoundFirewall, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: """Returns all action objects for a Firewall. :param firewall: :class:`BoundFirewall ` or :class:`Firewall ` @@ -157,7 +182,7 @@ class FirewallsClient(ClientEntityBase, GetEntityByNameMixin): Specifies how many results are returned by page :return: (List[:class:`BoundAction `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if status is not None: params["status"] = status if sort is not None: @@ -175,15 +200,14 @@ class FirewallsClient(ClientEntityBase, GetEntityByNameMixin): BoundAction(self._client.actions, action_data) for action_data in response["actions"] ] - return add_meta_to_result(actions, response, "actions") + return ActionsPageResult(actions, Meta.parse_meta(response)) def get_actions( self, - firewall, # type: Firewall - status=None, # type: Optional[List[str]] - sort=None, # type: Optional[List[str]] - ): - # type: (...) -> List[BoundAction] + firewall: Firewall | BoundFirewall, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: """Returns all action objects for a Firewall. :param firewall: :class:`BoundFirewall ` or :class:`Firewall ` @@ -194,10 +218,14 @@ class FirewallsClient(ClientEntityBase, GetEntityByNameMixin): :return: List[:class:`BoundAction `] """ - return super().get_actions(firewall, status=status, sort=sort) + return self._iter_pages( + self.get_actions_list, + firewall, + status=status, + sort=sort, + ) - def get_by_id(self, id): - # type: (int) -> BoundFirewall + def get_by_id(self, id: int) -> BoundFirewall: """Returns a specific Firewall object. :param id: int @@ -208,13 +236,12 @@ class FirewallsClient(ClientEntityBase, GetEntityByNameMixin): def get_list( self, - label_selector=None, # type: Optional[str] - page=None, # type: Optional[int] - per_page=None, # type: Optional[int] - name=None, # type: Optional[str] - sort=None, # type: Optional[List[str]] - ): - # type: (...) -> PageResults[List[BoundFirewall]] + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + name: str | None = None, + sort: list[str] | None = None, + ) -> FirewallsPageResult: """Get a list of floating ips from this account :param label_selector: str (optional) @@ -229,7 +256,7 @@ class FirewallsClient(ClientEntityBase, GetEntityByNameMixin): Choices: id name created (You can add one of ":asc", ":desc" to modify sort order. ( ":asc" is default)) :return: (List[:class:`BoundFirewall `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if label_selector is not None: params["label_selector"] = label_selector @@ -247,10 +274,14 @@ class FirewallsClient(ClientEntityBase, GetEntityByNameMixin): for firewall_data in response["firewalls"] ] - return self._add_meta_to_result(firewalls, response) + return FirewallsPageResult(firewalls, Meta.parse_meta(response)) - def get_all(self, label_selector=None, name=None, sort=None): - # type: (Optional[str], Optional[str], Optional[List[str]]) -> List[BoundFirewall] + def get_all( + self, + label_selector: str | None = None, + name: str | None = None, + sort: list[str] | None = None, + ) -> list[BoundFirewall]: """Get all floating ips from this account :param label_selector: str (optional) @@ -261,26 +292,29 @@ class FirewallsClient(ClientEntityBase, GetEntityByNameMixin): Choices: id name created (You can add one of ":asc", ":desc" to modify sort order. ( ":asc" is default)) :return: List[:class:`BoundFirewall `] """ - return super().get_all(label_selector=label_selector, name=name, sort=sort) + return self._iter_pages( + self.get_list, + label_selector=label_selector, + name=name, + sort=sort, + ) - def get_by_name(self, name): - # type: (str) -> BoundFirewall + def get_by_name(self, name: str) -> BoundFirewall | None: """Get Firewall by name :param name: str Used to get Firewall by name. :return: :class:`BoundFirewall ` """ - return super().get_by_name(name) + return self._get_first_by(name=name) def create( self, - name, # type: str - rules=None, # type: Optional[List[FirewallRule]] - labels=None, # type: Optional[str] - resources=None, # type: Optional[List[FirewallResource]] - ): - # type: (...) -> CreateFirewallResponse + name: str, + rules: list[FirewallRule] | None = None, + labels: str | None = None, + resources: list[FirewallResource] | None = None, + ) -> CreateFirewallResponse: """Creates a new Firewall. :param name: str @@ -292,7 +326,7 @@ class FirewallsClient(ClientEntityBase, GetEntityByNameMixin): :return: :class:`CreateFirewallResponse ` """ - data = {"name": name} + data: dict[str, Any] = {"name": name} if labels is not None: data["labels"] = labels @@ -309,7 +343,8 @@ class FirewallsClient(ClientEntityBase, GetEntityByNameMixin): actions = [] if response.get("actions") is not None: actions = [ - BoundAction(self._client.actions, _) for _ in response["actions"] + BoundAction(self._client.actions, action_data) + for action_data in response["actions"] ] result = CreateFirewallResponse( @@ -317,8 +352,12 @@ class FirewallsClient(ClientEntityBase, GetEntityByNameMixin): ) return result - def update(self, firewall, labels=None, name=None): - # type: (Firewall, Optional[Dict[str, str]], Optional[str]) -> BoundFirewall + def update( + self, + firewall: Firewall | BoundFirewall, + labels: dict[str, str] | None = None, + name: str | None = None, + ) -> BoundFirewall: """Updates the description or labels of a Firewall. :param firewall: :class:`BoundFirewall ` or :class:`Firewall ` @@ -328,7 +367,7 @@ class FirewallsClient(ClientEntityBase, GetEntityByNameMixin): New name to set :return: :class:`BoundFirewall ` """ - data = {} + data: dict[str, Any] = {} if labels is not None: data["labels"] = labels if name is not None: @@ -341,8 +380,7 @@ class FirewallsClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundFirewall(self, response["firewall"]) - def delete(self, firewall): - # type: (Firewall) -> bool + def delete(self, firewall: Firewall | BoundFirewall) -> bool: """Deletes a Firewall. :param firewall: :class:`BoundFirewall ` or :class:`Firewall ` @@ -355,62 +393,74 @@ class FirewallsClient(ClientEntityBase, GetEntityByNameMixin): # Return always true, because the API does not return an action for it. When an error occurs a HcloudAPIException will be raised return True - def set_rules(self, firewall, rules): - # type: (Firewall, List[FirewallRule]) -> List[BoundAction] + def set_rules( + self, + firewall: Firewall | BoundFirewall, + rules: list[FirewallRule], + ) -> list[BoundAction]: """Sets the rules of a Firewall. All existing rules will be overwritten. Pass an empty rules array to remove all rules. :param firewall: :class:`BoundFirewall ` or :class:`Firewall ` :param rules: List[:class:`FirewallRule `] :return: List[:class:`BoundAction `] """ - data = {"rules": []} + data: dict[str, Any] = {"rules": []} for rule in rules: data["rules"].append(rule.to_payload()) response = self._client.request( - url="/firewalls/{firewall_id}/actions/set_rules".format( - firewall_id=firewall.id - ), + url=f"/firewalls/{firewall.id}/actions/set_rules", method="POST", json=data, ) - return [BoundAction(self._client.actions, _) for _ in response["actions"]] + return [ + BoundAction(self._client.actions, action_data) + for action_data in response["actions"] + ] - def apply_to_resources(self, firewall, resources): - # type: (Firewall, List[FirewallResource]) -> List[BoundAction] + def apply_to_resources( + self, + firewall: Firewall | BoundFirewall, + resources: list[FirewallResource], + ) -> list[BoundAction]: """Applies one Firewall to multiple resources. :param firewall: :class:`BoundFirewall ` or :class:`Firewall ` :param resources: List[:class:`FirewallResource `] :return: List[:class:`BoundAction `] """ - data = {"apply_to": []} + data: dict[str, Any] = {"apply_to": []} for resource in resources: data["apply_to"].append(resource.to_payload()) response = self._client.request( - url="/firewalls/{firewall_id}/actions/apply_to_resources".format( - firewall_id=firewall.id - ), + url=f"/firewalls/{firewall.id}/actions/apply_to_resources", method="POST", json=data, ) - return [BoundAction(self._client.actions, _) for _ in response["actions"]] + return [ + BoundAction(self._client.actions, action_data) + for action_data in response["actions"] + ] - def remove_from_resources(self, firewall, resources): - # type: (Firewall, List[FirewallResource]) -> List[BoundAction] + def remove_from_resources( + self, + firewall: Firewall | BoundFirewall, + resources: list[FirewallResource], + ) -> list[BoundAction]: """Removes one Firewall from multiple resources. :param firewall: :class:`BoundFirewall ` or :class:`Firewall ` :param resources: List[:class:`FirewallResource `] :return: List[:class:`BoundAction `] """ - data = {"remove_from": []} + data: dict[str, Any] = {"remove_from": []} for resource in resources: data["remove_from"].append(resource.to_payload()) response = self._client.request( - url="/firewalls/{firewall_id}/actions/remove_from_resources".format( - firewall_id=firewall.id - ), + url=f"/firewalls/{firewall.id}/actions/remove_from_resources", method="POST", json=data, ) - return [BoundAction(self._client.actions, _) for _ in response["actions"]] + return [ + BoundAction(self._client.actions, action_data) + for action_data in response["actions"] + ] diff --git a/plugins/module_utils/vendor/hcloud/firewalls/domain.py b/plugins/module_utils/vendor/hcloud/firewalls/domain.py index 37e5cd5..0c3aa91 100644 --- a/plugins/module_utils/vendor/hcloud/firewalls/domain.py +++ b/plugins/module_utils/vendor/hcloud/firewalls/domain.py @@ -1,9 +1,18 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + try: from dateutil.parser import isoparse except ImportError: isoparse = None -from ..core.domain import BaseDomain +from ..core import BaseDomain + +if TYPE_CHECKING: + from ..actions import BoundAction + from ..servers import BoundServer, Server + from .client import BoundFirewall class Firewall(BaseDomain): @@ -26,7 +35,13 @@ class Firewall(BaseDomain): __slots__ = ("id", "name", "labels", "rules", "applied_to", "created") def __init__( - self, id=None, name=None, labels=None, rules=None, applied_to=None, created=None + self, + id: int | None = None, + name: str | None = None, + labels: dict[str, str] | None = None, + rules: list[FirewallRule] | None = None, + applied_to: list[FirewallResource] | None = None, + created: str | None = None, ): self.id = id self.name = name @@ -81,12 +96,12 @@ class FirewallRule: def __init__( self, - direction, # type: str - protocol, # type: str - source_ips, # type: List[str] - port=None, # type: Optional[str] - destination_ips=None, # type: Optional[List[str]] - description=None, # type: Optional[str] + direction: str, + protocol: str, + source_ips: list[str], + port: str | None = None, + destination_ips: list[str] | None = None, + description: str | None = None, ): self.direction = direction self.port = port @@ -95,18 +110,18 @@ class FirewallRule: self.destination_ips = destination_ips or [] self.description = description - def to_payload(self): - payload = { + def to_payload(self) -> dict[str, Any]: + payload: dict[str, Any] = { "direction": self.direction, "protocol": self.protocol, "source_ips": self.source_ips, } if len(self.destination_ips) > 0: - payload.update({"destination_ips": self.destination_ips}) + payload["destination_ips"] = self.destination_ips if self.port is not None: - payload.update({"port": self.port}) + payload["port"] = self.port if self.description is not None: - payload.update({"description": self.description}) + payload["description"] = self.description return payload @@ -130,23 +145,21 @@ class FirewallResource: def __init__( self, - type, # type: str - server=None, # type: Optional[Server] - label_selector=None, # type: Optional[FirewallResourceLabelSelector] + type: str, + server: Server | BoundServer | None = None, + label_selector: FirewallResourceLabelSelector | None = None, ): self.type = type self.server = server self.label_selector = label_selector - def to_payload(self): - payload = {"type": self.type} + def to_payload(self) -> dict[str, Any]: + payload: dict[str, Any] = {"type": self.type} if self.server is not None: - payload.update({"server": {"id": self.server.id}}) + payload["server"] = {"id": self.server.id} if self.label_selector is not None: - payload.update( - {"label_selector": {"selector": self.label_selector.selector}} - ) + payload["label_selector"] = {"selector": self.label_selector.selector} return payload @@ -156,7 +169,7 @@ class FirewallResourceLabelSelector(BaseDomain): :param selector: str Target label selector """ - def __init__(self, selector=None): + def __init__(self, selector: str | None = None): self.selector = selector @@ -173,8 +186,8 @@ class CreateFirewallResponse(BaseDomain): def __init__( self, - firewall, # type: BoundFirewall - actions, # type: BoundAction + firewall: BoundFirewall, + actions: list[BoundAction] | None, ): self.firewall = firewall self.actions = actions diff --git a/plugins/module_utils/vendor/hcloud/floating_ips/__init__.py b/plugins/module_utils/vendor/hcloud/floating_ips/__init__.py index e69de29..4e55bf5 100644 --- a/plugins/module_utils/vendor/hcloud/floating_ips/__init__.py +++ b/plugins/module_utils/vendor/hcloud/floating_ips/__init__.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .client import ( # noqa: F401 + BoundFloatingIP, + FloatingIPsClient, + FloatingIPsPageResult, +) +from .domain import CreateFloatingIPResponse, FloatingIP # noqa: F401 diff --git a/plugins/module_utils/vendor/hcloud/floating_ips/client.py b/plugins/module_utils/vendor/hcloud/floating_ips/client.py index d5d7a16..58d7fa3 100644 --- a/plugins/module_utils/vendor/hcloud/floating_ips/client.py +++ b/plugins/module_utils/vendor/hcloud/floating_ips/client.py @@ -1,15 +1,25 @@ -from ..actions.client import BoundAction -from ..core.client import BoundModelBase, ClientEntityBase, GetEntityByNameMixin -from ..core.domain import add_meta_to_result -from ..locations.client import BoundLocation +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..actions import ActionsPageResult, BoundAction +from ..core import BoundModelBase, ClientEntityBase, Meta +from ..locations import BoundLocation from .domain import CreateFloatingIPResponse, FloatingIP +if TYPE_CHECKING: + from .._client import Client + from ..locations import Location + from ..servers import BoundServer, Server + class BoundFloatingIP(BoundModelBase): + _client: FloatingIPsClient + model = FloatingIP - def __init__(self, client, data, complete=True): - from ..servers.client import BoundServer + def __init__(self, client: FloatingIPsClient, data: dict, complete: bool = True): + from ..servers import BoundServer server = data.get("server") if server is not None: @@ -25,8 +35,13 @@ class BoundFloatingIP(BoundModelBase): super().__init__(client, data, complete) - def get_actions_list(self, status=None, sort=None, page=None, per_page=None): - # type: (Optional[List[str]], Optional[List[str]], Optional[int], Optional[int]) -> PageResult[BoundAction, Meta] + def get_actions_list( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: """Returns all action objects for a Floating IP. :param status: List[str] (optional) @@ -41,8 +56,11 @@ class BoundFloatingIP(BoundModelBase): """ return self._client.get_actions_list(self, status, sort, page, per_page) - def get_actions(self, status=None, sort=None): - # type: (Optional[List[str]], Optional[List[str]]) -> List[BoundAction] + def get_actions( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: """Returns all action objects for a Floating IP. :param status: List[str] (optional) @@ -54,8 +72,12 @@ class BoundFloatingIP(BoundModelBase): """ return self._client.get_actions(self, status, sort) - def update(self, description=None, labels=None, name=None): - # type: (Optional[str], Optional[Dict[str, str]], Optional[str]) -> BoundFloatingIP + def update( + self, + description: str | None = None, + labels: dict[str, str] | None = None, + name: str | None = None, + ) -> BoundFloatingIP: """Updates the description or labels of a Floating IP. :param description: str (optional) @@ -68,16 +90,14 @@ class BoundFloatingIP(BoundModelBase): """ return self._client.update(self, description, labels, name) - def delete(self): - # type: () -> bool + def delete(self) -> bool: """Deletes a Floating IP. If it is currently assigned to a server it will automatically get unassigned. :return: boolean """ return self._client.delete(self) - def change_protection(self, delete=None): - # type: (Optional[bool]) -> BoundAction + def change_protection(self, delete: bool | None = None) -> BoundAction: """Changes the protection configuration of the Floating IP. :param delete: boolean @@ -86,8 +106,7 @@ class BoundFloatingIP(BoundModelBase): """ return self._client.change_protection(self, delete) - def assign(self, server): - # type: (Server) -> BoundAction + def assign(self, server: Server | BoundServer) -> BoundAction: """Assigns a Floating IP to a server. :param server: :class:`BoundServer ` or :class:`Server ` @@ -96,16 +115,14 @@ class BoundFloatingIP(BoundModelBase): """ return self._client.assign(self, server) - def unassign(self): - # type: () -> BoundAction + def unassign(self) -> BoundAction: """Unassigns a Floating IP, resulting in it being unreachable. You may assign it to a server again at a later time. :return: :class:`BoundAction ` """ return self._client.unassign(self) - def change_dns_ptr(self, ip, dns_ptr): - # type: (str, str) -> BoundAction + def change_dns_ptr(self, ip: str, dns_ptr: str) -> BoundAction: """Changes the hostname that will appear when getting the hostname belonging to this Floating IP. :param ip: str @@ -117,18 +134,22 @@ class BoundFloatingIP(BoundModelBase): return self._client.change_dns_ptr(self, ip, dns_ptr) -class FloatingIPsClient(ClientEntityBase, GetEntityByNameMixin): - results_list_attribute_name = "floating_ips" +class FloatingIPsPageResult(NamedTuple): + floating_ips: list[BoundFloatingIP] + meta: Meta | None + + +class FloatingIPsClient(ClientEntityBase): + _client: Client def get_actions_list( self, - floating_ip, # type: FloatingIP - status=None, # type: Optional[List[str]] - sort=None, # type: Optional[List[str]] - page=None, # type: Optional[int] - per_page=None, # type: Optional[int] - ): - # type: (...) -> PageResults[List[BoundAction], Meta] + floating_ip: FloatingIP | BoundFloatingIP, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: """Returns all action objects for a Floating IP. :param floating_ip: :class:`BoundFloatingIP ` or :class:`FloatingIP ` @@ -142,7 +163,7 @@ class FloatingIPsClient(ClientEntityBase, GetEntityByNameMixin): Specifies how many results are returned by page :return: (List[:class:`BoundAction `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if status is not None: params["status"] = status if sort is not None: @@ -152,9 +173,7 @@ class FloatingIPsClient(ClientEntityBase, GetEntityByNameMixin): if per_page is not None: params["per_page"] = per_page response = self._client.request( - url="/floating_ips/{floating_ip_id}/actions".format( - floating_ip_id=floating_ip.id - ), + url=f"/floating_ips/{floating_ip.id}/actions", method="GET", params=params, ) @@ -162,15 +181,14 @@ class FloatingIPsClient(ClientEntityBase, GetEntityByNameMixin): BoundAction(self._client.actions, action_data) for action_data in response["actions"] ] - return add_meta_to_result(actions, response, "actions") + return ActionsPageResult(actions, Meta.parse_meta(response)) def get_actions( self, - floating_ip, # type: FloatingIP - status=None, # type: Optional[List[str]] - sort=None, # type: Optional[List[str]] - ): - # type: (...) -> List[BoundAction] + floating_ip: FloatingIP | BoundFloatingIP, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: """Returns all action objects for a Floating IP. :param floating_ip: :class:`BoundFloatingIP ` or :class:`FloatingIP ` @@ -181,10 +199,14 @@ class FloatingIPsClient(ClientEntityBase, GetEntityByNameMixin): :return: List[:class:`BoundAction `] """ - return super().get_actions(floating_ip, status=status, sort=sort) + return self._iter_pages( + self.get_actions_list, + floating_ip, + status=status, + sort=sort, + ) - def get_by_id(self, id): - # type: (int) -> BoundFloatingIP + def get_by_id(self, id: int) -> BoundFloatingIP: """Returns a specific Floating IP object. :param id: int @@ -195,12 +217,11 @@ class FloatingIPsClient(ClientEntityBase, GetEntityByNameMixin): def get_list( self, - label_selector=None, # type: Optional[str] - page=None, # type: Optional[int] - per_page=None, # type: Optional[int] - name=None, # type: Optional[str] - ): - # type: (...) -> PageResults[List[BoundFloatingIP]] + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + name: str | None = None, + ) -> FloatingIPsPageResult: """Get a list of floating ips from this account :param label_selector: str (optional) @@ -213,7 +234,7 @@ class FloatingIPsClient(ClientEntityBase, GetEntityByNameMixin): Can be used to filter networks by their name. :return: (List[:class:`BoundFloatingIP `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if label_selector is not None: params["label_selector"] = label_selector @@ -232,10 +253,13 @@ class FloatingIPsClient(ClientEntityBase, GetEntityByNameMixin): for floating_ip_data in response["floating_ips"] ] - return self._add_meta_to_result(floating_ips, response) + return FloatingIPsPageResult(floating_ips, Meta.parse_meta(response)) - def get_all(self, label_selector=None, name=None): - # type: (Optional[str], Optional[str]) -> List[BoundFloatingIP] + def get_all( + self, + label_selector: str | None = None, + name: str | None = None, + ) -> list[BoundFloatingIP]: """Get all floating ips from this account :param label_selector: str (optional) @@ -244,28 +268,26 @@ class FloatingIPsClient(ClientEntityBase, GetEntityByNameMixin): Can be used to filter networks by their name. :return: List[:class:`BoundFloatingIP `] """ - return super().get_all(label_selector=label_selector, name=name) + return self._iter_pages(self.get_list, label_selector=label_selector, name=name) - def get_by_name(self, name): - # type: (str) -> BoundFloatingIP + def get_by_name(self, name: str) -> BoundFloatingIP | None: """Get Floating IP by name :param name: str Used to get Floating IP by name. :return: :class:`BoundFloatingIP ` """ - return super().get_by_name(name) + return self._get_first_by(name=name) def create( self, - type, # type: str - description=None, # type: Optional[str] - labels=None, # type: Optional[str] - home_location=None, # type: Optional[Location] - server=None, # type: Optional[Server] - name=None, # type: Optional[str] - ): - # type: (...) -> CreateFloatingIPResponse + type: str, + description: str | None = None, + labels: str | None = None, + home_location: Location | BoundLocation | None = None, + server: Server | BoundServer | None = None, + name: str | None = None, + ) -> CreateFloatingIPResponse: """Creates a new Floating IP assigned to a server. :param type: str @@ -281,7 +303,7 @@ class FloatingIPsClient(ClientEntityBase, GetEntityByNameMixin): :return: :class:`CreateFloatingIPResponse ` """ - data = {"type": type} + data: dict[str, Any] = {"type": type} if description is not None: data["description"] = description if labels is not None: @@ -304,8 +326,13 @@ class FloatingIPsClient(ClientEntityBase, GetEntityByNameMixin): ) return result - def update(self, floating_ip, description=None, labels=None, name=None): - # type: (FloatingIP, Optional[str], Optional[Dict[str, str]], Optional[str]) -> BoundFloatingIP + def update( + self, + floating_ip: FloatingIP | BoundFloatingIP, + description: str | None = None, + labels: dict[str, str] | None = None, + name: str | None = None, + ) -> BoundFloatingIP: """Updates the description or labels of a Floating IP. :param floating_ip: :class:`BoundFloatingIP ` or :class:`FloatingIP ` @@ -317,7 +344,7 @@ class FloatingIPsClient(ClientEntityBase, GetEntityByNameMixin): New name to set :return: :class:`BoundFloatingIP ` """ - data = {} + data: dict[str, Any] = {} if description is not None: data["description"] = description if labels is not None: @@ -332,8 +359,7 @@ class FloatingIPsClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundFloatingIP(self, response["floating_ip"]) - def delete(self, floating_ip): - # type: (FloatingIP) -> bool + def delete(self, floating_ip: FloatingIP | BoundFloatingIP) -> bool: """Deletes a Floating IP. If it is currently assigned to a server it will automatically get unassigned. :param floating_ip: :class:`BoundFloatingIP ` or :class:`FloatingIP ` @@ -346,8 +372,11 @@ class FloatingIPsClient(ClientEntityBase, GetEntityByNameMixin): # Return always true, because the API does not return an action for it. When an error occurs a HcloudAPIException will be raised return True - def change_protection(self, floating_ip, delete=None): - # type: (FloatingIP, Optional[bool]) -> BoundAction + def change_protection( + self, + floating_ip: FloatingIP | BoundFloatingIP, + delete: bool | None = None, + ) -> BoundAction: """Changes the protection configuration of the Floating IP. :param floating_ip: :class:`BoundFloatingIP ` or :class:`FloatingIP ` @@ -355,21 +384,22 @@ class FloatingIPsClient(ClientEntityBase, GetEntityByNameMixin): If true, prevents the Floating IP from being deleted :return: :class:`BoundAction ` """ - data = {} + data: dict[str, Any] = {} if delete is not None: data.update({"delete": delete}) response = self._client.request( - url="/floating_ips/{floating_ip_id}/actions/change_protection".format( - floating_ip_id=floating_ip.id - ), + url=f"/floating_ips/{floating_ip.id}/actions/change_protection", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def assign(self, floating_ip, server): - # type: (FloatingIP, Server) -> BoundAction + def assign( + self, + floating_ip: FloatingIP | BoundFloatingIP, + server: Server | BoundServer, + ) -> BoundAction: """Assigns a Floating IP to a server. :param floating_ip: :class:`BoundFloatingIP ` or :class:`FloatingIP ` @@ -378,31 +408,30 @@ class FloatingIPsClient(ClientEntityBase, GetEntityByNameMixin): :return: :class:`BoundAction ` """ response = self._client.request( - url="/floating_ips/{floating_ip_id}/actions/assign".format( - floating_ip_id=floating_ip.id - ), + url=f"/floating_ips/{floating_ip.id}/actions/assign", method="POST", json={"server": server.id}, ) return BoundAction(self._client.actions, response["action"]) - def unassign(self, floating_ip): - # type: (FloatingIP) -> BoundAction + def unassign(self, floating_ip: FloatingIP | BoundFloatingIP) -> BoundAction: """Unassigns a Floating IP, resulting in it being unreachable. You may assign it to a server again at a later time. :param floating_ip: :class:`BoundFloatingIP ` or :class:`FloatingIP ` :return: :class:`BoundAction ` """ response = self._client.request( - url="/floating_ips/{floating_ip_id}/actions/unassign".format( - floating_ip_id=floating_ip.id - ), + url=f"/floating_ips/{floating_ip.id}/actions/unassign", method="POST", ) return BoundAction(self._client.actions, response["action"]) - def change_dns_ptr(self, floating_ip, ip, dns_ptr): - # type: (FloatingIP, str, str) -> BoundAction + def change_dns_ptr( + self, + floating_ip: FloatingIP | BoundFloatingIP, + ip: str, + dns_ptr: str, + ) -> BoundAction: """Changes the hostname that will appear when getting the hostname belonging to this Floating IP. :param floating_ip: :class:`BoundFloatingIP ` or :class:`FloatingIP ` @@ -413,9 +442,7 @@ class FloatingIPsClient(ClientEntityBase, GetEntityByNameMixin): :return: :class:`BoundAction ` """ response = self._client.request( - url="/floating_ips/{floating_ip_id}/actions/change_dns_ptr".format( - floating_ip_id=floating_ip.id - ), + url=f"/floating_ips/{floating_ip.id}/actions/change_dns_ptr", method="POST", json={"ip": ip, "dns_ptr": dns_ptr}, ) diff --git a/plugins/module_utils/vendor/hcloud/floating_ips/domain.py b/plugins/module_utils/vendor/hcloud/floating_ips/domain.py index a1ccfda..e1f295b 100644 --- a/plugins/module_utils/vendor/hcloud/floating_ips/domain.py +++ b/plugins/module_utils/vendor/hcloud/floating_ips/domain.py @@ -1,9 +1,19 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + try: from dateutil.parser import isoparse except ImportError: isoparse = None -from ..core.domain import BaseDomain +from ..core import BaseDomain + +if TYPE_CHECKING: + from ..actions import BoundAction + from ..locations import BoundLocation + from ..servers import BoundServer + from .client import BoundFloatingIP class FloatingIP(BaseDomain): @@ -52,18 +62,18 @@ class FloatingIP(BaseDomain): def __init__( self, - id=None, - type=None, - description=None, - ip=None, - server=None, - dns_ptr=None, - home_location=None, - blocked=None, - protection=None, - labels=None, - created=None, - name=None, + id: int | None = None, + type: str | None = None, + description: str | None = None, + ip: str | None = None, + server: BoundServer | None = None, + dns_ptr: list[dict] | None = None, + home_location: BoundLocation | None = None, + blocked: bool | None = None, + protection: dict | None = None, + labels: dict[str, str] | None = None, + created: str | None = None, + name: str | None = None, ): self.id = id self.type = type @@ -92,8 +102,8 @@ class CreateFloatingIPResponse(BaseDomain): def __init__( self, - floating_ip, # type: BoundFloatingIP - action, # type: BoundAction + floating_ip: BoundFloatingIP, + action: BoundAction | None, ): self.floating_ip = floating_ip self.action = action diff --git a/plugins/module_utils/vendor/hcloud/hcloud.py b/plugins/module_utils/vendor/hcloud/hcloud.py index af4e5c2..df67a5b 100644 --- a/plugins/module_utils/vendor/hcloud/hcloud.py +++ b/plugins/module_utils/vendor/hcloud/hcloud.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import warnings warnings.warn( diff --git a/plugins/module_utils/vendor/hcloud/helpers/__init__.py b/plugins/module_utils/vendor/hcloud/helpers/__init__.py index e69de29..b6a4cd7 100644 --- a/plugins/module_utils/vendor/hcloud/helpers/__init__.py +++ b/plugins/module_utils/vendor/hcloud/helpers/__init__.py @@ -0,0 +1,3 @@ +from __future__ import annotations + +from .labels import LabelValidator # noqa: F401 diff --git a/plugins/module_utils/vendor/hcloud/helpers/labels.py b/plugins/module_utils/vendor/hcloud/helpers/labels.py index 515982f..d5af8bc 100644 --- a/plugins/module_utils/vendor/hcloud/helpers/labels.py +++ b/plugins/module_utils/vendor/hcloud/helpers/labels.py @@ -1,5 +1,6 @@ +from __future__ import annotations + import re -from typing import Dict class LabelValidator: @@ -11,7 +12,7 @@ class LabelValidator: ) @staticmethod - def validate(labels: Dict[str, str]) -> bool: + def validate(labels: dict[str, str]) -> bool: """Validates Labels. If you want to know which key/value pair of the dict is not correctly formatted use :func:`~hcloud.helpers.labels.validate_verbose`. @@ -25,7 +26,7 @@ class LabelValidator: return True @staticmethod - def validate_verbose(labels: Dict[str, str]) -> (bool, str): + def validate_verbose(labels: dict[str, str]) -> tuple[bool, str]: """Validates Labels and returns the corresponding error message if something is wrong. Returns True, if everything is fine. diff --git a/plugins/module_utils/vendor/hcloud/images/__init__.py b/plugins/module_utils/vendor/hcloud/images/__init__.py index e69de29..78cb686 100644 --- a/plugins/module_utils/vendor/hcloud/images/__init__.py +++ b/plugins/module_utils/vendor/hcloud/images/__init__.py @@ -0,0 +1,4 @@ +from __future__ import annotations + +from .client import BoundImage, ImagesClient, ImagesPageResult # noqa: F401 +from .domain import CreateImageResponse, Image # noqa: F401 diff --git a/plugins/module_utils/vendor/hcloud/images/client.py b/plugins/module_utils/vendor/hcloud/images/client.py index 30c10af..a7dbef7 100644 --- a/plugins/module_utils/vendor/hcloud/images/client.py +++ b/plugins/module_utils/vendor/hcloud/images/client.py @@ -1,14 +1,22 @@ -from ..actions.client import BoundAction -from ..core.client import BoundModelBase, ClientEntityBase, GetEntityByNameMixin -from ..core.domain import add_meta_to_result +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..actions import ActionsPageResult, BoundAction +from ..core import BoundModelBase, ClientEntityBase, Meta from .domain import Image +if TYPE_CHECKING: + from .._client import Client + class BoundImage(BoundModelBase): + _client: ImagesClient + model = Image - def __init__(self, client, data): - from ..servers.client import BoundServer + def __init__(self, client: ImagesClient, data: dict): + from ..servers import BoundServer created_from = data.get("created_from") if created_from is not None: @@ -23,8 +31,13 @@ class BoundImage(BoundModelBase): super().__init__(client, data) - def get_actions_list(self, sort=None, page=None, per_page=None, status=None): - # type: (Optional[List[str]], Optional[int], Optional[int], Optional[List[str]]) -> PageResult[BoundAction, Meta] + def get_actions_list( + self, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + status: list[str] | None = None, + ) -> ActionsPageResult: """Returns a list of action objects for the image. :param status: List[str] (optional) @@ -41,8 +54,11 @@ class BoundImage(BoundModelBase): self, sort=sort, page=page, per_page=per_page, status=status ) - def get_actions(self, sort=None, status=None): - # type: (Optional[List[str]], Optional[List[str]]) -> List[BoundAction] + def get_actions( + self, + sort: list[str] | None = None, + status: list[str] | None = None, + ) -> list[BoundAction]: """Returns all action objects for the image. :param status: List[str] (optional) @@ -53,8 +69,12 @@ class BoundImage(BoundModelBase): """ return self._client.get_actions(self, status=status, sort=sort) - def update(self, description=None, type=None, labels=None): - # type: (Optional[str], Optional[str], Optional[Dict[str, str]]) -> BoundImage + def update( + self, + description: str | None = None, + type: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundImage: """Updates the Image. You may change the description, convert a Backup image to a Snapshot Image or change the image labels. :param description: str (optional) @@ -68,16 +88,14 @@ class BoundImage(BoundModelBase): """ return self._client.update(self, description, type, labels) - def delete(self): - # type: () -> bool + def delete(self) -> bool: """Deletes an Image. Only images of type snapshot and backup can be deleted. :return: bool """ return self._client.delete(self) - def change_protection(self, delete=None): - # type: (Optional[bool]) -> BoundAction + def change_protection(self, delete: bool | None = None) -> BoundAction: """Changes the protection configuration of the image. Can only be used on snapshots. :param delete: bool @@ -87,18 +105,22 @@ class BoundImage(BoundModelBase): return self._client.change_protection(self, delete) -class ImagesClient(ClientEntityBase, GetEntityByNameMixin): - results_list_attribute_name = "images" +class ImagesPageResult(NamedTuple): + images: list[BoundImage] + meta: Meta | None + + +class ImagesClient(ClientEntityBase): + _client: Client def get_actions_list( self, - image, # type: Image - sort=None, # type: Optional[List[str]] - page=None, # type: Optional[int] - per_page=None, # type: Optional[int] - status=None, # type: Optional[List[str]] - ): - # type: (...) -> PageResults[List[BoundAction], Meta] + image: Image | BoundImage, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + status: list[str] | None = None, + ) -> ActionsPageResult: """Returns a list of action objects for an image. :param image: :class:`BoundImage ` or :class:`Image ` @@ -112,7 +134,7 @@ class ImagesClient(ClientEntityBase, GetEntityByNameMixin): Specifies how many results are returned by page :return: (List[:class:`BoundAction `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if sort is not None: params["sort"] = sort if status is not None: @@ -130,15 +152,14 @@ class ImagesClient(ClientEntityBase, GetEntityByNameMixin): BoundAction(self._client.actions, action_data) for action_data in response["actions"] ] - return add_meta_to_result(actions, response, "actions") + return ActionsPageResult(actions, Meta.parse_meta(response)) def get_actions( self, - image, # type: Image - sort=None, # type: Optional[List[str]] - status=None, # type: Optional[List[str]] - ): - # type: (...) -> List[BoundAction] + image: Image | BoundImage, + sort: list[str] | None = None, + status: list[str] | None = None, + ) -> list[BoundAction]: """Returns all action objects for an image. :param image: :class:`BoundImage ` or :class:`Image ` @@ -148,10 +169,14 @@ class ImagesClient(ClientEntityBase, GetEntityByNameMixin): Specify how the results are sorted. Choices: `id` `command` `status` `progress` `started` `finished` . You can add one of ":asc", ":desc" to modify sort order. ( ":asc" is default) :return: List[:class:`BoundAction `] """ - return super().get_actions(image, sort=sort, status=status) + return self._iter_pages( + self.get_actions_list, + image, + sort=sort, + status=status, + ) - def get_by_id(self, id): - # type: (int) -> BoundImage + def get_by_id(self, id: int) -> BoundImage: """Get a specific Image :param id: int @@ -162,18 +187,17 @@ class ImagesClient(ClientEntityBase, GetEntityByNameMixin): def get_list( self, - name=None, # type: Optional[str] - label_selector=None, # type: Optional[str] - bound_to=None, # type: Optional[List[str]] - type=None, # type: Optional[List[str]] - architecture=None, # type: Optional[List[str]] - sort=None, # type: Optional[List[str]] - page=None, # type: Optional[int] - per_page=None, # type: Optional[int] - status=None, # type: Optional[List[str]] - include_deprecated=None, # type: Optional[bool] - ): - # type: (...) -> PageResults[List[BoundImage]] + name: str | None = None, + label_selector: str | None = None, + bound_to: list[str] | None = None, + type: list[str] | None = None, + architecture: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + status: list[str] | None = None, + include_deprecated: bool | None = None, + ) -> ImagesPageResult: """Get all images :param name: str (optional) @@ -198,7 +222,7 @@ class ImagesClient(ClientEntityBase, GetEntityByNameMixin): Specifies how many results are returned by page :return: (List[:class:`BoundImage `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if name is not None: params["name"] = name if label_selector is not None: @@ -222,20 +246,19 @@ class ImagesClient(ClientEntityBase, GetEntityByNameMixin): response = self._client.request(url="/images", method="GET", params=params) images = [BoundImage(self, image_data) for image_data in response["images"]] - return self._add_meta_to_result(images, response) + return ImagesPageResult(images, Meta.parse_meta(response)) def get_all( self, - name=None, # type: Optional[str] - label_selector=None, # type: Optional[str] - bound_to=None, # type: Optional[List[str]] - type=None, # type: Optional[List[str]] - architecture=None, # type: Optional[List[str]] - sort=None, # type: Optional[List[str]] - status=None, # type: Optional[List[str]] - include_deprecated=None, # type: Optional[bool] - ): - # type: (...) -> List[BoundImage] + name: str | None = None, + label_selector: str | None = None, + bound_to: list[str] | None = None, + type: list[str] | None = None, + architecture: list[str] | None = None, + sort: list[str] | None = None, + status: list[str] | None = None, + include_deprecated: bool | None = None, + ) -> list[BoundImage]: """Get all images :param name: str (optional) @@ -256,7 +279,8 @@ class ImagesClient(ClientEntityBase, GetEntityByNameMixin): Include deprecated images in the response. Default: False :return: List[:class:`BoundImage `] """ - return super().get_all( + return self._iter_pages( + self.get_list, name=name, label_selector=label_selector, bound_to=bound_to, @@ -267,8 +291,7 @@ class ImagesClient(ClientEntityBase, GetEntityByNameMixin): include_deprecated=include_deprecated, ) - def get_by_name(self, name): - # type: (str) -> BoundImage + def get_by_name(self, name: str) -> BoundImage | None: """Get image by name Deprecated: Use get_by_name_and_architecture instead. @@ -277,10 +300,13 @@ class ImagesClient(ClientEntityBase, GetEntityByNameMixin): Used to get image by name. :return: :class:`BoundImage ` """ - return super().get_by_name(name) + return self._get_first_by(name=name) - def get_by_name_and_architecture(self, name, architecture): - # type: (str, str) -> BoundImage + def get_by_name_and_architecture( + self, + name: str, + architecture: str, + ) -> BoundImage | None: """Get image by name :param name: str @@ -289,13 +315,15 @@ class ImagesClient(ClientEntityBase, GetEntityByNameMixin): Used to identify the image. :return: :class:`BoundImage ` """ - response = self.get_list(name=name, architecture=[architecture]) - entities = getattr(response, self.results_list_attribute_name) - entity = entities[0] if entities else None - return entity + return self._get_first_by(name=name, architecture=[architecture]) - def update(self, image, description=None, type=None, labels=None): - # type:(Image, Optional[str], Optional[str], Optional[Dict[str, str]]) -> BoundImage + def update( + self, + image: Image | BoundImage, + description: str | None = None, + type: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundImage: """Updates the Image. You may change the description, convert a Backup image to a Snapshot Image or change the image labels. :param image: :class:`BoundImage ` or :class:`Image ` @@ -308,7 +336,7 @@ class ImagesClient(ClientEntityBase, GetEntityByNameMixin): User-defined labels (key-value pairs) :return: :class:`BoundImage ` """ - data = {} + data: dict[str, Any] = {} if description is not None: data.update({"description": description}) if type is not None: @@ -320,8 +348,7 @@ class ImagesClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundImage(self, response["image"]) - def delete(self, image): - # type: (Image) -> bool + def delete(self, image: Image | BoundImage) -> bool: """Deletes an Image. Only images of type snapshot and backup can be deleted. :param :class:`BoundImage ` or :class:`Image ` @@ -331,8 +358,11 @@ class ImagesClient(ClientEntityBase, GetEntityByNameMixin): # Return allays true, because the API does not return an action for it. When an error occurs a APIException will be raised return True - def change_protection(self, image, delete=None): - # type: (Image, Optional[bool]) -> BoundAction + def change_protection( + self, + image: Image | BoundImage, + delete: bool | None = None, + ) -> BoundAction: """Changes the protection configuration of the image. Can only be used on snapshots. :param image: :class:`BoundImage ` or :class:`Image ` @@ -340,14 +370,12 @@ class ImagesClient(ClientEntityBase, GetEntityByNameMixin): If true, prevents the snapshot from being deleted :return: :class:`BoundAction ` """ - data = {} + data: dict[str, Any] = {} if delete is not None: data.update({"delete": delete}) response = self._client.request( - url="/images/{image_id}/actions/change_protection".format( - image_id=image.id - ), + url=f"/images/{image.id}/actions/change_protection", method="POST", json=data, ) diff --git a/plugins/module_utils/vendor/hcloud/images/domain.py b/plugins/module_utils/vendor/hcloud/images/domain.py index 746acfd..e55de7f 100644 --- a/plugins/module_utils/vendor/hcloud/images/domain.py +++ b/plugins/module_utils/vendor/hcloud/images/domain.py @@ -1,9 +1,18 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + try: from dateutil.parser import isoparse except ImportError: isoparse = None -from ..core.domain import BaseDomain, DomainIdentityMixin +from ..core import BaseDomain, DomainIdentityMixin + +if TYPE_CHECKING: + from ..actions import BoundAction + from ..servers import BoundServer, Server + from .client import BoundImage class Image(BaseDomain, DomainIdentityMixin): @@ -67,23 +76,23 @@ class Image(BaseDomain, DomainIdentityMixin): def __init__( self, - id=None, - name=None, - type=None, - created=None, - description=None, - image_size=None, - disk_size=None, - deprecated=None, - bound_to=None, - os_flavor=None, - os_version=None, - architecture=None, - rapid_deploy=None, - created_from=None, - protection=None, - labels=None, - status=None, + id: int | None = None, + name: str | None = None, + type: str | None = None, + created: str | None = None, + description: str | None = None, + image_size: int | None = None, + disk_size: int | None = None, + deprecated: str | None = None, + bound_to: Server | BoundServer | None = None, + os_flavor: str | None = None, + os_version: str | None = None, + architecture: str | None = None, + rapid_deploy: bool | None = None, + created_from: Server | BoundServer | None = None, + protection: dict | None = None, + labels: dict[str, str] | None = None, + status: str | None = None, ): self.id = id self.name = name @@ -117,8 +126,8 @@ class CreateImageResponse(BaseDomain): def __init__( self, - action, # type: BoundAction - image, # type: BoundImage + action: BoundAction, + image: BoundImage, ): self.action = action self.image = image diff --git a/plugins/module_utils/vendor/hcloud/isos/__init__.py b/plugins/module_utils/vendor/hcloud/isos/__init__.py index e69de29..0d5e38f 100644 --- a/plugins/module_utils/vendor/hcloud/isos/__init__.py +++ b/plugins/module_utils/vendor/hcloud/isos/__init__.py @@ -0,0 +1,4 @@ +from __future__ import annotations + +from .client import BoundIso, IsosClient, IsosPageResult # noqa: F401 +from .domain import Iso # noqa: F401 diff --git a/plugins/module_utils/vendor/hcloud/isos/client.py b/plugins/module_utils/vendor/hcloud/isos/client.py index 4ca9528..1ab5fc9 100644 --- a/plugins/module_utils/vendor/hcloud/isos/client.py +++ b/plugins/module_utils/vendor/hcloud/isos/client.py @@ -1,18 +1,30 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple from warnings import warn -from ..core.client import BoundModelBase, ClientEntityBase, GetEntityByNameMixin +from ..core import BoundModelBase, ClientEntityBase, Meta from .domain import Iso +if TYPE_CHECKING: + from .._client import Client + class BoundIso(BoundModelBase): + _client: IsosClient + model = Iso -class IsosClient(ClientEntityBase, GetEntityByNameMixin): - results_list_attribute_name = "isos" +class IsosPageResult(NamedTuple): + isos: list[BoundIso] + meta: Meta | None - def get_by_id(self, id): - # type: (int) -> BoundIso + +class IsosClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundIso: """Get a specific ISO by its id :param id: int @@ -23,14 +35,13 @@ class IsosClient(ClientEntityBase, GetEntityByNameMixin): def get_list( self, - name=None, # type: Optional[str] - architecture=None, # type: Optional[List[str]] - include_wildcard_architecture=None, # type: Optional[bool] - include_architecture_wildcard=None, # type: Optional[bool] - page=None, # type: Optional[int] - per_page=None, # type: Optional[int] - ): - # type: (...) -> PageResults[List[BoundIso], Meta] + name: str | None = None, + architecture: list[str] | None = None, + include_wildcard_architecture: bool | None = None, + include_architecture_wildcard: bool | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> IsosPageResult: """Get a list of ISOs :param name: str (optional) @@ -56,7 +67,7 @@ class IsosClient(ClientEntityBase, GetEntityByNameMixin): ) include_architecture_wildcard = include_wildcard_architecture - params = {} + params: dict[str, Any] = {} if name is not None: params["name"] = name if architecture is not None: @@ -70,16 +81,15 @@ class IsosClient(ClientEntityBase, GetEntityByNameMixin): response = self._client.request(url="/isos", method="GET", params=params) isos = [BoundIso(self, iso_data) for iso_data in response["isos"]] - return self._add_meta_to_result(isos, response) + return IsosPageResult(isos, Meta.parse_meta(response)) def get_all( self, - name=None, # type: Optional[str] - architecture=None, # type: Optional[List[str]] - include_wildcard_architecture=None, # type: Optional[bool] - include_architecture_wildcard=None, # type: Optional[bool] - ): - # type: (...) -> List[BoundIso] + name: str | None = None, + architecture: list[str] | None = None, + include_wildcard_architecture: bool | None = None, + include_architecture_wildcard: bool | None = None, + ) -> list[BoundIso]: """Get all ISOs :param name: str (optional) @@ -101,18 +111,18 @@ class IsosClient(ClientEntityBase, GetEntityByNameMixin): ) include_architecture_wildcard = include_wildcard_architecture - return super().get_all( + return self._iter_pages( + self.get_list, name=name, architecture=architecture, include_architecture_wildcard=include_architecture_wildcard, ) - def get_by_name(self, name): - # type: (str) -> BoundIso + def get_by_name(self, name: str) -> BoundIso | None: """Get iso by name :param name: str Used to get iso by name. :return: :class:`BoundIso ` """ - return super().get_by_name(name) + return self._get_first_by(name=name) diff --git a/plugins/module_utils/vendor/hcloud/isos/domain.py b/plugins/module_utils/vendor/hcloud/isos/domain.py index 4431129..99be0a0 100644 --- a/plugins/module_utils/vendor/hcloud/isos/domain.py +++ b/plugins/module_utils/vendor/hcloud/isos/domain.py @@ -1,9 +1,11 @@ +from __future__ import annotations + try: from dateutil.parser import isoparse except ImportError: isoparse = None -from ..core.domain import BaseDomain, DomainIdentityMixin +from ..core import BaseDomain, DomainIdentityMixin class Iso(BaseDomain, DomainIdentityMixin): @@ -27,12 +29,12 @@ class Iso(BaseDomain, DomainIdentityMixin): def __init__( self, - id=None, - name=None, - type=None, - architecture=None, - description=None, - deprecated=None, + id: int | None = None, + name: str | None = None, + type: str | None = None, + architecture: str | None = None, + description: str | None = None, + deprecated: str | None = None, ): self.id = id self.name = name diff --git a/plugins/module_utils/vendor/hcloud/load_balancer_types/__init__.py b/plugins/module_utils/vendor/hcloud/load_balancer_types/__init__.py index e69de29..fa1dc33 100644 --- a/plugins/module_utils/vendor/hcloud/load_balancer_types/__init__.py +++ b/plugins/module_utils/vendor/hcloud/load_balancer_types/__init__.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .client import ( # noqa: F401 + BoundLoadBalancerType, + LoadBalancerTypesClient, + LoadBalancerTypesPageResult, +) +from .domain import LoadBalancerType # noqa: F401 diff --git a/plugins/module_utils/vendor/hcloud/load_balancer_types/client.py b/plugins/module_utils/vendor/hcloud/load_balancer_types/client.py index 405e2b5..fa91c01 100644 --- a/plugins/module_utils/vendor/hcloud/load_balancer_types/client.py +++ b/plugins/module_utils/vendor/hcloud/load_balancer_types/client.py @@ -1,31 +1,46 @@ -from ..core.client import BoundModelBase, ClientEntityBase, GetEntityByNameMixin +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..core import BoundModelBase, ClientEntityBase, Meta from .domain import LoadBalancerType +if TYPE_CHECKING: + from .._client import Client + class BoundLoadBalancerType(BoundModelBase): + _client: LoadBalancerTypesClient + model = LoadBalancerType -class LoadBalancerTypesClient(ClientEntityBase, GetEntityByNameMixin): - results_list_attribute_name = "load_balancer_types" +class LoadBalancerTypesPageResult(NamedTuple): + load_balancer_types: list[BoundLoadBalancerType] + meta: Meta | None - def get_by_id(self, id): - # type: (int) -> load_balancer_types.client.BoundLoadBalancerType + +class LoadBalancerTypesClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundLoadBalancerType: """Returns a specific Load Balancer Type. :param id: int :return: :class:`BoundLoadBalancerType ` """ response = self._client.request( - url="/load_balancer_types/{load_balancer_type_id}".format( - load_balancer_type_id=id - ), + url=f"/load_balancer_types/{id}", method="GET", ) return BoundLoadBalancerType(self, response["load_balancer_type"]) - def get_list(self, name=None, page=None, per_page=None): - # type: (Optional[str], Optional[int], Optional[int]) -> PageResults[List[BoundLoadBalancerType], Meta] + def get_list( + self, + name: str | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> LoadBalancerTypesPageResult: """Get a list of Load Balancer types :param name: str (optional) @@ -36,7 +51,7 @@ class LoadBalancerTypesClient(ClientEntityBase, GetEntityByNameMixin): Specifies how many results are returned by page :return: (List[:class:`BoundLoadBalancerType `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if name is not None: params["name"] = name if page is not None: @@ -51,24 +66,24 @@ class LoadBalancerTypesClient(ClientEntityBase, GetEntityByNameMixin): BoundLoadBalancerType(self, load_balancer_type_data) for load_balancer_type_data in response["load_balancer_types"] ] - return self._add_meta_to_result(load_balancer_types, response) + return LoadBalancerTypesPageResult( + load_balancer_types, Meta.parse_meta(response) + ) - def get_all(self, name=None): - # type: (Optional[str]) -> List[BoundLoadBalancerType] + def get_all(self, name: str | None = None) -> list[BoundLoadBalancerType]: """Get all Load Balancer types :param name: str (optional) Can be used to filter Load Balancer type by their name. :return: List[:class:`BoundLoadBalancerType `] """ - return super().get_all(name=name) + return self._iter_pages(self.get_list, name=name) - def get_by_name(self, name): - # type: (str) -> BoundLoadBalancerType + def get_by_name(self, name: str) -> BoundLoadBalancerType | None: """Get Load Balancer type by name :param name: str Used to get Load Balancer type by name. :return: :class:`BoundLoadBalancerType ` """ - return super().get_by_name(name) + return self._get_first_by(name=name) diff --git a/plugins/module_utils/vendor/hcloud/load_balancer_types/domain.py b/plugins/module_utils/vendor/hcloud/load_balancer_types/domain.py index 7bdb1c0..35719db 100644 --- a/plugins/module_utils/vendor/hcloud/load_balancer_types/domain.py +++ b/plugins/module_utils/vendor/hcloud/load_balancer_types/domain.py @@ -1,4 +1,6 @@ -from ..core.domain import BaseDomain, DomainIdentityMixin +from __future__ import annotations + +from ..core import BaseDomain, DomainIdentityMixin class LoadBalancerType(BaseDomain, DomainIdentityMixin): @@ -36,14 +38,14 @@ class LoadBalancerType(BaseDomain, DomainIdentityMixin): def __init__( self, - id=None, - name=None, - description=None, - max_connections=None, - max_services=None, - max_targets=None, - max_assigned_certificates=None, - prices=None, + id: int | None = None, + name: str | None = None, + description: str | None = None, + max_connections: int | None = None, + max_services: int | None = None, + max_targets: int | None = None, + max_assigned_certificates: int | None = None, + prices: dict | None = None, ): self.id = id self.name = name diff --git a/plugins/module_utils/vendor/hcloud/load_balancers/__init__.py b/plugins/module_utils/vendor/hcloud/load_balancers/__init__.py index e69de29..76106a5 100644 --- a/plugins/module_utils/vendor/hcloud/load_balancers/__init__.py +++ b/plugins/module_utils/vendor/hcloud/load_balancers/__init__.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +from .client import ( # noqa: F401 + BoundLoadBalancer, + LoadBalancersClient, + LoadBalancersPageResult, +) +from .domain import ( # noqa: F401 + CreateLoadBalancerResponse, + IPv4Address, + IPv6Network, + LoadBalancer, + LoadBalancerAlgorithm, + LoadBalancerHealtCheckHttp, + LoadBalancerHealthCheck, + LoadBalancerService, + LoadBalancerServiceHttp, + LoadBalancerTarget, + LoadBalancerTargetIP, + LoadBalancerTargetLabelSelector, + PrivateNet, + PublicNetwork, +) diff --git a/plugins/module_utils/vendor/hcloud/load_balancers/client.py b/plugins/module_utils/vendor/hcloud/load_balancers/client.py index 25cc134..e228cc2 100644 --- a/plugins/module_utils/vendor/hcloud/load_balancers/client.py +++ b/plugins/module_utils/vendor/hcloud/load_balancers/client.py @@ -1,11 +1,14 @@ -from ..actions.client import BoundAction -from ..certificates.client import BoundCertificate -from ..core.client import BoundModelBase, ClientEntityBase, GetEntityByNameMixin -from ..core.domain import add_meta_to_result -from ..load_balancer_types.client import BoundLoadBalancerType -from ..locations.client import BoundLocation -from ..networks.client import BoundNetwork -from ..servers.client import BoundServer +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..actions import ActionsPageResult, BoundAction +from ..certificates import BoundCertificate +from ..core import BoundModelBase, ClientEntityBase, Meta +from ..load_balancer_types import BoundLoadBalancerType +from ..locations import BoundLocation +from ..networks import BoundNetwork +from ..servers import BoundServer from .domain import ( CreateLoadBalancerResponse, IPv4Address, @@ -23,11 +26,19 @@ from .domain import ( PublicNetwork, ) +if TYPE_CHECKING: + from .._client import Client + from ..load_balancer_types import LoadBalancerType + from ..locations import Location + from ..networks import Network + class BoundLoadBalancer(BoundModelBase): + _client: LoadBalancersClient + model = LoadBalancer - def __init__(self, client, data, complete=True): + def __init__(self, client: LoadBalancersClient, data: dict, complete: bool = True): algorithm = data.get("algorithm") if algorithm: data["algorithm"] = LoadBalancerAlgorithm(type=algorithm["type"]) @@ -131,8 +142,11 @@ class BoundLoadBalancer(BoundModelBase): super().__init__(client, data, complete) - def update(self, name=None, labels=None): - # type: (Optional[str], Optional[Dict[str, str]]) -> BoundLoadBalancer + def update( + self, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundLoadBalancer: """Updates a Load Balancer. You can update a Load Balancers name and a Load Balancers labels. :param name: str (optional) @@ -143,16 +157,20 @@ class BoundLoadBalancer(BoundModelBase): """ return self._client.update(self, name, labels) - def delete(self): - # type: () -> BoundAction + def delete(self) -> bool: """Deletes a Load Balancer. :return: boolean """ return self._client.delete(self) - def get_actions_list(self, status=None, sort=None, page=None, per_page=None): - # type: (Optional[List[str]], Optional[List[str]], Optional[int], Optional[int]) -> PageResults[List[BoundAction, Meta]] + def get_actions_list( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: """Returns all action objects for a Load Balancer. :param status: List[str] (optional) @@ -167,8 +185,11 @@ class BoundLoadBalancer(BoundModelBase): """ return self._client.get_actions_list(self, status, sort, page, per_page) - def get_actions(self, status=None, sort=None): - # type: (Optional[List[str]], Optional[List[str]]) -> List[BoundAction] + def get_actions( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: """Returns all action objects for a Load Balancer. :param status: List[str] (optional) @@ -179,8 +200,7 @@ class BoundLoadBalancer(BoundModelBase): """ return self._client.get_actions(self, status, sort) - def add_service(self, service): - # type: (LoadBalancerService) -> List[BoundAction] + def add_service(self, service: LoadBalancerService) -> BoundAction: """Adds a service to a Load Balancer. :param service: :class:`LoadBalancerService ` @@ -189,8 +209,7 @@ class BoundLoadBalancer(BoundModelBase): """ return self._client.add_service(self, service=service) - def update_service(self, service): - # type: (LoadBalancerService) -> List[BoundAction] + def update_service(self, service: LoadBalancerService) -> BoundAction: """Updates a service of an Load Balancer. :param service: :class:`LoadBalancerService ` @@ -199,8 +218,7 @@ class BoundLoadBalancer(BoundModelBase): """ return self._client.update_service(self, service=service) - def delete_service(self, service): - # type: (LoadBalancerService) -> List[BoundAction] + def delete_service(self, service: LoadBalancerService) -> BoundAction: """Deletes a service from a Load Balancer. :param service: :class:`LoadBalancerService ` @@ -209,8 +227,7 @@ class BoundLoadBalancer(BoundModelBase): """ return self._client.delete_service(self, service) - def add_target(self, target): - # type: (LoadBalancerTarget) -> List[BoundAction] + def add_target(self, target: LoadBalancerTarget) -> BoundAction: """Adds a target to a Load Balancer. :param target: :class:`LoadBalancerTarget ` @@ -219,8 +236,7 @@ class BoundLoadBalancer(BoundModelBase): """ return self._client.add_target(self, target) - def remove_target(self, target): - # type: (LoadBalancerTarget) -> List[BoundAction] + def remove_target(self, target: LoadBalancerTarget) -> BoundAction: """Removes a target from a Load Balancer. :param target: :class:`LoadBalancerTarget ` @@ -229,8 +245,7 @@ class BoundLoadBalancer(BoundModelBase): """ return self._client.remove_target(self, target) - def change_algorithm(self, algorithm): - # type: (LoadBalancerAlgorithm) -> List[BoundAction] + def change_algorithm(self, algorithm: LoadBalancerAlgorithm) -> BoundAction: """Changes the algorithm used by the Load Balancer :param algorithm: :class:`LoadBalancerAlgorithm ` @@ -239,8 +254,7 @@ class BoundLoadBalancer(BoundModelBase): """ return self._client.change_algorithm(self, algorithm) - def change_dns_ptr(self, ip, dns_ptr): - # type: (str, str) -> BoundAction + def change_dns_ptr(self, ip: str, dns_ptr: str) -> BoundAction: """Changes the hostname that will appear when getting the hostname belonging to the public IPs (IPv4 and IPv6) of this Load Balancer. :param ip: str @@ -251,8 +265,7 @@ class BoundLoadBalancer(BoundModelBase): """ return self._client.change_dns_ptr(self, ip, dns_ptr) - def change_protection(self, delete): - # type: (LoadBalancerService) -> List[BoundAction] + def change_protection(self, delete: bool) -> BoundAction: """Changes the protection configuration of a Load Balancer. :param delete: boolean @@ -261,8 +274,11 @@ class BoundLoadBalancer(BoundModelBase): """ return self._client.change_protection(self, delete) - def attach_to_network(self, network, ip=None): - # type: (Union[Network,BoundNetwork],Optional[str]) -> BoundAction + def attach_to_network( + self, + network: Network | BoundNetwork, + ip: str | None = None, + ) -> BoundAction: """Attaches a Load Balancer to a Network :param network: :class:`BoundNetwork ` or :class:`Network ` @@ -272,8 +288,7 @@ class BoundLoadBalancer(BoundModelBase): """ return self._client.attach_to_network(self, network, ip) - def detach_from_network(self, network): - # type: ( Union[Network,BoundNetwork]) -> BoundAction + def detach_from_network(self, network: Network | BoundNetwork) -> BoundAction: """Detaches a Load Balancer from a Network. :param network: :class:`BoundNetwork ` or :class:`Network ` @@ -281,24 +296,24 @@ class BoundLoadBalancer(BoundModelBase): """ return self._client.detach_from_network(self, network) - def enable_public_interface(self): - # type: () -> BoundAction + def enable_public_interface(self) -> BoundAction: """Enables the public interface of a Load Balancer. :return: :class:`BoundAction ` """ return self._client.enable_public_interface(self) - def disable_public_interface(self): - # type: () -> BoundAction + def disable_public_interface(self) -> BoundAction: """Disables the public interface of a Load Balancer. :return: :class:`BoundAction ` """ return self._client.disable_public_interface(self) - def change_type(self, load_balancer_type): - # type: (Union[LoadBalancerType,BoundLoadBalancerType]) -> BoundAction + def change_type( + self, + load_balancer_type: LoadBalancerType | BoundLoadBalancerType, + ) -> BoundAction: """Changes the type of a Load Balancer. :param load_balancer_type: :class:`BoundLoadBalancerType ` or :class:`LoadBalancerType ` @@ -308,11 +323,15 @@ class BoundLoadBalancer(BoundModelBase): return self._client.change_type(self, load_balancer_type) -class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): - results_list_attribute_name = "load_balancers" +class LoadBalancersPageResult(NamedTuple): + load_balancers: list[BoundLoadBalancer] + meta: Meta | None - def get_by_id(self, id): - # type: (int) -> BoundLoadBalancer + +class LoadBalancersClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundLoadBalancer: """Get a specific Load Balancer :param id: int @@ -326,12 +345,11 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): def get_list( self, - name=None, # type: Optional[str] - label_selector=None, # type: Optional[str] - page=None, # type: Optional[int] - per_page=None, # type: Optional[int] - ): - # type: (...) -> PageResults[List[BoundLoadBalancer], Meta] + name: str | None = None, + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> LoadBalancersPageResult: """Get a list of Load Balancers from this account :param name: str (optional) @@ -344,7 +362,7 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): Specifies how many results are returned by page :return: (List[:class:`BoundLoadBalancer `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if name is not None: params["name"] = name if label_selector is not None: @@ -358,14 +376,17 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): url="/load_balancers", method="GET", params=params ) - ass_load_balancers = [ + load_balancers = [ BoundLoadBalancer(self, load_balancer_data) for load_balancer_data in response["load_balancers"] ] - return self._add_meta_to_result(ass_load_balancers, response) + return LoadBalancersPageResult(load_balancers, Meta.parse_meta(response)) - def get_all(self, name=None, label_selector=None): - # type: (Optional[str], Optional[str]) -> List[BoundLoadBalancer] + def get_all( + self, + name: str | None = None, + label_selector: str | None = None, + ) -> list[BoundLoadBalancer]: """Get all Load Balancers from this account :param name: str (optional) @@ -374,32 +395,30 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): Can be used to filter Load Balancers by labels. The response will only contain Load Balancers matching the label selector. :return: List[:class:`BoundLoadBalancer `] """ - return super().get_all(name=name, label_selector=label_selector) + return self._iter_pages(self.get_list, name=name, label_selector=label_selector) - def get_by_name(self, name): - # type: (str) -> BoundLoadBalancer + def get_by_name(self, name: str) -> BoundLoadBalancer | None: """Get Load Balancer by name :param name: str Used to get Load Balancer by name. :return: :class:`BoundLoadBalancer ` """ - return super().get_by_name(name) + return self._get_first_by(name=name) def create( self, - name, # type: str - load_balancer_type, # type: LoadBalancerType - algorithm=None, # type: Optional[LoadBalancerAlgorithm] - services=None, # type: Optional[List[LoadBalancerService]] - targets=None, # type: Optional[List[LoadBalancerTarget]] - labels=None, # type: Optional[Dict[str, str]] - location=None, # type: Optional[Location] - network_zone=None, # type: Optional[str] - public_interface=None, # type: Optional[bool] - network=None, # type: Optional[Union[Network,BoundNetwork]] - ): - # type: (...) -> CreateLoadBalancerResponse + name: str, + load_balancer_type: LoadBalancerType | BoundLoadBalancerType, + algorithm: LoadBalancerAlgorithm | None = None, + services: list[LoadBalancerService] | None = None, + targets: list[LoadBalancerTarget] | None = None, + labels: dict[str, str] | None = None, + location: Location | BoundLocation | None = None, + network_zone: str | None = None, + public_interface: bool | None = None, + network: Network | BoundNetwork | None = None, + ) -> CreateLoadBalancerResponse: """Creates a Load Balancer . :param name: str @@ -424,7 +443,10 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): Adds the Load Balancer to a Network :return: :class:`CreateLoadBalancerResponse ` """ - data = {"name": name, "load_balancer_type": load_balancer_type.id_or_name} + data: dict[str, Any] = { + "name": name, + "load_balancer_type": load_balancer_type.id_or_name, + } if network is not None: data["network"] = network.id if public_interface is not None: @@ -434,30 +456,9 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): if algorithm is not None: data["algorithm"] = {"type": algorithm.type} if services is not None: - service_list = [] - for service in services: - service_list.append(self.get_service_parameters(service)) - data["services"] = service_list - + data["services"] = [service.to_payload() for service in services] if targets is not None: - target_list = [] - for target in targets: - target_data = { - "type": target.type, - "use_private_ip": target.use_private_ip, - } - if target.type == "server": - target_data["server"] = {"id": target.server.id} - elif target.type == "label_selector": - target_data["label_selector"] = { - "selector": target.label_selector.selector - } - elif target.type == "ip": - target_data["ip"] = {"ip": target.ip.ip} - target_list.append(target_data) - - data["targets"] = target_list - + data["targets"] = [target.to_payload() for target in targets] if network_zone is not None: data["network_zone"] = network_zone if location is not None: @@ -470,8 +471,12 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): action=BoundAction(self._client.actions, response["action"]), ) - def update(self, load_balancer, name=None, labels=None): - # type:(LoadBalancer, Optional[str], Optional[Dict[str, str]]) -> BoundLoadBalancer + def update( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundLoadBalancer: """Updates a LoadBalancer. You can update a LoadBalancer’s name and a LoadBalancer’s labels. :param load_balancer: :class:`BoundLoadBalancer ` or :class:`LoadBalancer ` @@ -481,39 +486,38 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): User-defined labels (key-value pairs) :return: :class:`BoundLoadBalancer ` """ - data = {} + data: dict[str, Any] = {} if name is not None: data.update({"name": name}) if labels is not None: data.update({"labels": labels}) response = self._client.request( - url="/load_balancers/{load_balancer_id}".format( - load_balancer_id=load_balancer.id - ), + url=f"/load_balancers/{load_balancer.id}", method="PUT", json=data, ) return BoundLoadBalancer(self, response["load_balancer"]) - def delete(self, load_balancer): - # type: (LoadBalancer) -> BoundAction + def delete(self, load_balancer: LoadBalancer | BoundLoadBalancer) -> bool: """Deletes a Load Balancer. :param load_balancer: :class:`BoundLoadBalancer ` or :class:`LoadBalancer ` :return: boolean """ self._client.request( - url="/load_balancers/{load_balancer_id}".format( - load_balancer_id=load_balancer.id - ), + url=f"/load_balancers/{load_balancer.id}", method="DELETE", ) return True def get_actions_list( - self, load_balancer, status=None, sort=None, page=None, per_page=None - ): - # type: (LoadBalancer, Optional[List[str]], Optional[List[str]], Optional[int], Optional[int]) -> PageResults[List[BoundAction], Meta] + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: """Returns all action objects for a Load Balancer. :param load_balancer: :class:`BoundLoadBalancer ` or :class:`LoadBalancer ` @@ -527,7 +531,7 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): Specifies how many results are returned by page :return: (List[:class:`BoundAction `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if status is not None: params["status"] = status if sort is not None: @@ -538,9 +542,7 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): params["per_page"] = per_page response = self._client.request( - url="/load_balancers/{load_balancer_id}/actions".format( - load_balancer_id=load_balancer.id - ), + url=f"/load_balancers/{load_balancer.id}/actions", method="GET", params=params, ) @@ -548,10 +550,14 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): BoundAction(self._client.actions, action_data) for action_data in response["actions"] ] - return add_meta_to_result(actions, response, "actions") + return ActionsPageResult(actions, Meta.parse_meta(response)) - def get_actions(self, load_balancer, status=None, sort=None): - # type: (LoadBalancer, Optional[List[str]], Optional[List[str]]) -> List[BoundAction] + def get_actions( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: """Returns all action objects for a Load Balancer. :param load_balancer: :class:`BoundLoadBalancer ` or :class:`LoadBalancer ` @@ -561,10 +567,18 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` :return: List[:class:`BoundAction `] """ - return super().get_actions(load_balancer, status=status, sort=sort) + return self._iter_pages( + self.get_actions_list, + load_balancer, + status=status, + sort=sort, + ) - def add_service(self, load_balancer, service): - # type: (Union[LoadBalancer, BoundLoadBalancer], LoadBalancerService) -> List[BoundAction] + def add_service( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + service: LoadBalancerService, + ) -> BoundAction: """Adds a service to a Load Balancer. :param load_balancer: :class:`BoundLoadBalancer ` or :class:`LoadBalancer ` @@ -572,84 +586,20 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): The LoadBalancerService you want to add to the Load Balancer :return: :class:`BoundAction ` """ - data = self.get_service_parameters(service) + data: dict[str, Any] = service.to_payload() response = self._client.request( - url="/load_balancers/{load_balancer_id}/actions/add_service".format( - load_balancer_id=load_balancer.id - ), + url=f"/load_balancers/{load_balancer.id}/actions/add_service", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def get_service_parameters(self, service): - data = {} - if service.protocol is not None: - data["protocol"] = service.protocol - if service.listen_port is not None: - data["listen_port"] = service.listen_port - if service.destination_port is not None: - data["destination_port"] = service.destination_port - if service.proxyprotocol is not None: - data["proxyprotocol"] = service.proxyprotocol - if service.http is not None: - data["http"] = {} - if service.http.cookie_name is not None: - data["http"]["cookie_name"] = service.http.cookie_name - if service.http.cookie_lifetime is not None: - data["http"]["cookie_lifetime"] = service.http.cookie_lifetime - if service.http.redirect_http is not None: - data["http"]["redirect_http"] = service.http.redirect_http - if service.http.sticky_sessions is not None: - data["http"]["sticky_sessions"] = service.http.sticky_sessions - certificate_ids = [] - for certificate in service.http.certificates: - certificate_ids.append(certificate.id) - data["http"]["certificates"] = certificate_ids - if service.health_check is not None: - data["health_check"] = { - "protocol": service.health_check.protocol, - "port": service.health_check.port, - "interval": service.health_check.interval, - "timeout": service.health_check.timeout, - "retries": service.health_check.retries, - } - data["health_check"] = {} - if service.health_check.protocol is not None: - data["health_check"]["protocol"] = service.health_check.protocol - if service.health_check.port is not None: - data["health_check"]["port"] = service.health_check.port - if service.health_check.interval is not None: - data["health_check"]["interval"] = service.health_check.interval - if service.health_check.timeout is not None: - data["health_check"]["timeout"] = service.health_check.timeout - if service.health_check.retries is not None: - data["health_check"]["retries"] = service.health_check.retries - if service.health_check.http is not None: - data["health_check"]["http"] = {} - if service.health_check.http.domain is not None: - data["health_check"]["http"][ - "domain" - ] = service.health_check.http.domain - if service.health_check.http.path is not None: - data["health_check"]["http"][ - "path" - ] = service.health_check.http.path - if service.health_check.http.response is not None: - data["health_check"]["http"][ - "response" - ] = service.health_check.http.response - if service.health_check.http.status_codes is not None: - data["health_check"]["http"][ - "status_codes" - ] = service.health_check.http.status_codes - if service.health_check.http.tls is not None: - data["health_check"]["http"]["tls"] = service.health_check.http.tls - return data - - def update_service(self, load_balancer, service): - # type: (Union[LoadBalancer, BoundLoadBalancer], LoadBalancerService) -> List[BoundAction] + def update_service( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + service: LoadBalancerService, + ) -> BoundAction: """Updates a service of an Load Balancer. :param load_balancer: :class:`BoundLoadBalancer ` or :class:`LoadBalancer ` @@ -657,18 +607,19 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): The LoadBalancerService with updated values within for the Load Balancer :return: :class:`BoundAction ` """ - data = self.get_service_parameters(service) + data: dict[str, Any] = service.to_payload() response = self._client.request( - url="/load_balancers/{load_balancer_id}/actions/update_service".format( - load_balancer_id=load_balancer.id - ), + url=f"/load_balancers/{load_balancer.id}/actions/update_service", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def delete_service(self, load_balancer, service): - # type: (Union[LoadBalancer, BoundLoadBalancer], LoadBalancerService) -> List[BoundAction] + def delete_service( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + service: LoadBalancerService, + ) -> BoundAction: """Deletes a service from a Load Balancer. :param load_balancer: :class:`BoundLoadBalancer ` or :class:`LoadBalancer ` @@ -676,19 +627,20 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): The LoadBalancerService you want to delete from the Load Balancer :return: :class:`BoundAction ` """ - data = {"listen_port": service.listen_port} + data: dict[str, Any] = {"listen_port": service.listen_port} response = self._client.request( - url="/load_balancers/{load_balancer_id}/actions/delete_service".format( - load_balancer_id=load_balancer.id - ), + url=f"/load_balancers/{load_balancer.id}/actions/delete_service", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def add_target(self, load_balancer, target): - # type: (Union[LoadBalancer, BoundLoadBalancer], LoadBalancerTarget) -> List[BoundAction] + def add_target( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + target: LoadBalancerTarget, + ) -> BoundAction: """Adds a target to a Load Balancer. :param load_balancer: :class:`BoundLoadBalancer ` or :class:`LoadBalancer ` @@ -696,25 +648,20 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): The LoadBalancerTarget you want to add to the Load Balancer :return: :class:`BoundAction ` """ - data = {"type": target.type, "use_private_ip": target.use_private_ip} - if target.type == "server": - data["server"] = {"id": target.server.id} - elif target.type == "label_selector": - data["label_selector"] = {"selector": target.label_selector.selector} - elif target.type == "ip": - data["ip"] = {"ip": target.ip.ip} + data: dict[str, Any] = target.to_payload() response = self._client.request( - url="/load_balancers/{load_balancer_id}/actions/add_target".format( - load_balancer_id=load_balancer.id - ), + url=f"/load_balancers/{load_balancer.id}/actions/add_target", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def remove_target(self, load_balancer, target): - # type: (Union[LoadBalancer, BoundLoadBalancer], LoadBalancerTarget) -> List[BoundAction] + def remove_target( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + target: LoadBalancerTarget, + ) -> BoundAction: """Removes a target from a Load Balancer. :param load_balancer: :class:`BoundLoadBalancer ` or :class:`LoadBalancer ` @@ -722,25 +669,22 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): The LoadBalancerTarget you want to remove from the Load Balancer :return: :class:`BoundAction ` """ - data = {"type": target.type} - if target.type == "server": - data["server"] = {"id": target.server.id} - elif target.type == "label_selector": - data["label_selector"] = {"selector": target.label_selector.selector} - elif target.type == "ip": - data["ip"] = {"ip": target.ip.ip} + data: dict[str, Any] = target.to_payload() + # Do not send use_private_ip on remove_target + data.pop("use_private_ip", None) response = self._client.request( - url="/load_balancers/{load_balancer_id}/actions/remove_target".format( - load_balancer_id=load_balancer.id - ), + url=f"/load_balancers/{load_balancer.id}/actions/remove_target", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def change_algorithm(self, load_balancer, algorithm): - # type: (Union[LoadBalancer, BoundLoadBalancer], Optional[bool]) -> BoundAction + def change_algorithm( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + algorithm: LoadBalancerAlgorithm, + ) -> BoundAction: """Changes the algorithm used by the Load Balancer :param load_balancer: :class:` ` or :class:`LoadBalancer ` @@ -748,19 +692,21 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): The LoadBalancerSubnet you want to add to the Load Balancer :return: :class:`BoundAction ` """ - data = {"type": algorithm.type} + data: dict[str, Any] = {"type": algorithm.type} response = self._client.request( - url="/load_balancers/{load_balancer_id}/actions/change_algorithm".format( - load_balancer_id=load_balancer.id - ), + url=f"/load_balancers/{load_balancer.id}/actions/change_algorithm", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def change_dns_ptr(self, load_balancer, ip, dns_ptr): - # type: (Union[LoadBalancer, BoundLoadBalancer], str, str) -> BoundAction + def change_dns_ptr( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + ip: str, + dns_ptr: str, + ) -> BoundAction: """Changes the hostname that will appear when getting the hostname belonging to the public IPs (IPv4 and IPv6) of this Load Balancer. :param ip: str @@ -771,16 +717,17 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): """ response = self._client.request( - url="/load_balancers/{load_balancer_id}/actions/change_dns_ptr".format( - load_balancer_id=load_balancer.id - ), + url=f"/load_balancers/{load_balancer.id}/actions/change_dns_ptr", method="POST", json={"ip": ip, "dns_ptr": dns_ptr}, ) return BoundAction(self._client.actions, response["action"]) - def change_protection(self, load_balancer, delete=None): - # type: (Union[LoadBalancer, BoundLoadBalancer], Optional[bool]) -> BoundAction + def change_protection( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + delete: bool | None = None, + ) -> BoundAction: """Changes the protection configuration of a Load Balancer. :param load_balancer: :class:` ` or :class:`LoadBalancer ` @@ -788,14 +735,12 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): If True, prevents the Load Balancer from being deleted :return: :class:`BoundAction ` """ - data = {} + data: dict[str, Any] = {} if delete is not None: data.update({"delete": delete}) response = self._client.request( - url="/load_balancers/{load_balancer_id}/actions/change_protection".format( - load_balancer_id=load_balancer.id - ), + url=f"/load_balancers/{load_balancer.id}/actions/change_protection", method="POST", json=data, ) @@ -803,10 +748,10 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): def attach_to_network( self, - load_balancer, # type: Union[LoadBalancer, BoundLoadBalancer] - network, # type: Union[Network, BoundNetwork] - ip=None, # type: Optional[str] - ): + load_balancer: LoadBalancer | BoundLoadBalancer, + network: Network | BoundNetwork, + ip: str | None = None, + ) -> BoundAction: """Attach a Load Balancer to a Network. :param load_balancer: :class:` ` or :class:`LoadBalancer ` @@ -815,39 +760,40 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): IP to request to be assigned to this Load Balancer :return: :class:`BoundAction ` """ - data = {"network": network.id} + data: dict[str, Any] = {"network": network.id} if ip is not None: data.update({"ip": ip}) response = self._client.request( - url="/load_balancers/{load_balancer_id}/actions/attach_to_network".format( - load_balancer_id=load_balancer.id - ), + url=f"/load_balancers/{load_balancer.id}/actions/attach_to_network", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def detach_from_network(self, load_balancer, network): - # type: (Union[LoadBalancer, BoundLoadBalancer], Union[Network,BoundNetwork]) -> BoundAction + def detach_from_network( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + network: Network | BoundNetwork, + ) -> BoundAction: """Detaches a Load Balancer from a Network. :param load_balancer: :class:` ` or :class:`LoadBalancer ` :param network: :class:`BoundNetwork ` or :class:`Network ` :return: :class:`BoundAction ` """ - data = {"network": network.id} + data: dict[str, Any] = {"network": network.id} response = self._client.request( - url="/load_balancers/{load_balancer_id}/actions/detach_from_network".format( - load_balancer_id=load_balancer.id - ), + url=f"/load_balancers/{load_balancer.id}/actions/detach_from_network", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def enable_public_interface(self, load_balancer): - # type: (Union[LoadBalancer, BoundLoadBalancer]) -> BoundAction + def enable_public_interface( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + ) -> BoundAction: """Enables the public interface of a Load Balancer. :param load_balancer: :class:` ` or :class:`LoadBalancer ` @@ -856,15 +802,15 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): """ response = self._client.request( - url="/load_balancers/{load_balancer_id}/actions/enable_public_interface".format( - load_balancer_id=load_balancer.id - ), + url=f"/load_balancers/{load_balancer.id}/actions/enable_public_interface", method="POST", ) return BoundAction(self._client.actions, response["action"]) - def disable_public_interface(self, load_balancer): - # type: (Union[LoadBalancer, BoundLoadBalancer]) -> BoundAction + def disable_public_interface( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + ) -> BoundAction: """Disables the public interface of a Load Balancer. :param load_balancer: :class:` ` or :class:`LoadBalancer ` @@ -873,15 +819,16 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): """ response = self._client.request( - url="/load_balancers/{load_balancer_id}/actions/disable_public_interface".format( - load_balancer_id=load_balancer.id - ), + url=f"/load_balancers/{load_balancer.id}/actions/disable_public_interface", method="POST", ) return BoundAction(self._client.actions, response["action"]) - def change_type(self, load_balancer, load_balancer_type): - # type: ([LoadBalancer, BoundLoadBalancer], [LoadBalancerType, BoundLoadBalancerType]) ->BoundAction + def change_type( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + load_balancer_type: LoadBalancerType | BoundLoadBalancerType, + ) -> BoundAction: """Changes the type of a Load Balancer. :param load_balancer: :class:`BoundLoadBalancer ` or :class:`LoadBalancer ` @@ -889,11 +836,9 @@ class LoadBalancersClient(ClientEntityBase, GetEntityByNameMixin): Load Balancer type the Load Balancer should migrate to :return: :class:`BoundAction ` """ - data = {"load_balancer_type": load_balancer_type.id_or_name} + data: dict[str, Any] = {"load_balancer_type": load_balancer_type.id_or_name} response = self._client.request( - url="/load_balancers/{load_balancer_id}/actions/change_type".format( - load_balancer_id=load_balancer.id - ), + url=f"/load_balancers/{load_balancer.id}/actions/change_type", method="POST", json=data, ) diff --git a/plugins/module_utils/vendor/hcloud/load_balancers/domain.py b/plugins/module_utils/vendor/hcloud/load_balancers/domain.py index f1769bf..6df6c9f 100644 --- a/plugins/module_utils/vendor/hcloud/load_balancers/domain.py +++ b/plugins/module_utils/vendor/hcloud/load_balancers/domain.py @@ -1,9 +1,22 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + try: from dateutil.parser import isoparse except ImportError: isoparse = None -from ..core.domain import BaseDomain +from ..core import BaseDomain + +if TYPE_CHECKING: + from ..actions import BoundAction + from ..certificates import BoundCertificate + from ..load_balancer_types import BoundLoadBalancerType + from ..locations import BoundLocation + from ..networks import BoundNetwork + from ..servers import BoundServer + from .client import BoundLoadBalancer class LoadBalancer(BaseDomain): @@ -61,21 +74,21 @@ class LoadBalancer(BaseDomain): def __init__( self, - id, - name=None, - public_net=None, - private_net=None, - location=None, - algorithm=None, - services=None, - load_balancer_type=None, - protection=None, - labels=None, - targets=None, - created=None, - outgoing_traffic=None, - ingoing_traffic=None, - included_traffic=None, + id: int, + name: str | None = None, + public_net: PublicNetwork | None = None, + private_net: PrivateNet | None = None, + location: BoundLocation | None = None, + algorithm: LoadBalancerAlgorithm | None = None, + services: list[LoadBalancerService] | None = None, + load_balancer_type: BoundLoadBalancerType | None = None, + protection: dict | None = None, + labels: dict[str, str] | None = None, + targets: list[LoadBalancerTarget] | None = None, + created: str | None = None, + outgoing_traffic: int | None = None, + ingoing_traffic: int | None = None, + included_traffic: int | None = None, ): self.id = id self.name = name @@ -113,12 +126,12 @@ class LoadBalancerService(BaseDomain): def __init__( self, - protocol=None, - listen_port=None, - destination_port=None, - proxyprotocol=None, - health_check=None, - http=None, + protocol: str | None = None, + listen_port: int | None = None, + destination_port: int | None = None, + proxyprotocol: bool | None = None, + health_check: LoadBalancerHealthCheck | None = None, + http: LoadBalancerServiceHttp | None = None, ): self.protocol = protocol self.listen_port = listen_port @@ -127,6 +140,74 @@ class LoadBalancerService(BaseDomain): self.health_check = health_check self.http = http + def to_payload(self) -> dict[str, Any]: + payload: dict[str, Any] = {} + + if self.protocol is not None: + payload["protocol"] = self.protocol + if self.listen_port is not None: + payload["listen_port"] = self.listen_port + if self.destination_port is not None: + payload["destination_port"] = self.destination_port + if self.proxyprotocol is not None: + payload["proxyprotocol"] = self.proxyprotocol + + if self.http is not None: + http: dict[str, Any] = {} + if self.http.cookie_name is not None: + http["cookie_name"] = self.http.cookie_name + if self.http.cookie_lifetime is not None: + http["cookie_lifetime"] = self.http.cookie_lifetime + if self.http.redirect_http is not None: + http["redirect_http"] = self.http.redirect_http + if self.http.sticky_sessions is not None: + http["sticky_sessions"] = self.http.sticky_sessions + + http["certificates"] = [ + certificate.id for certificate in self.http.certificates or [] + ] + + payload["http"] = http + + if self.health_check is not None: + health_check: dict[str, Any] = { + "protocol": self.health_check.protocol, + "port": self.health_check.port, + "interval": self.health_check.interval, + "timeout": self.health_check.timeout, + "retries": self.health_check.retries, + } + if self.health_check.protocol is not None: + health_check["protocol"] = self.health_check.protocol + if self.health_check.port is not None: + health_check["port"] = self.health_check.port + if self.health_check.interval is not None: + health_check["interval"] = self.health_check.interval + if self.health_check.timeout is not None: + health_check["timeout"] = self.health_check.timeout + if self.health_check.retries is not None: + health_check["retries"] = self.health_check.retries + + if self.health_check.http is not None: + health_check_http: dict[str, Any] = {} + if self.health_check.http.domain is not None: + health_check_http["domain"] = self.health_check.http.domain + if self.health_check.http.path is not None: + health_check_http["path"] = self.health_check.http.path + 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 + if self.health_check.http.tls is not None: + health_check_http["tls"] = self.health_check.http.tls + + health_check["http"] = health_check_http + + payload["health_check"] = health_check + return payload + class LoadBalancerServiceHttp(BaseDomain): """LoadBalancerServiceHttp Domain @@ -145,11 +226,11 @@ class LoadBalancerServiceHttp(BaseDomain): def __init__( self, - cookie_name=None, - cookie_lifetime=None, - certificates=None, - redirect_http=None, - sticky_sessions=None, + cookie_name: str | None = None, + cookie_lifetime: str | None = None, + certificates: list[BoundCertificate] | None = None, + redirect_http: bool | None = None, + sticky_sessions: bool | None = None, ): self.cookie_name = cookie_name self.cookie_lifetime = cookie_lifetime @@ -177,12 +258,12 @@ class LoadBalancerHealthCheck(BaseDomain): def __init__( self, - protocol=None, - port=None, - interval=None, - timeout=None, - retries=None, - http=None, + protocol: str | None = None, + port: int | None = None, + interval: int | None = None, + timeout: int | None = None, + retries: int | None = None, + http: LoadBalancerHealtCheckHttp | None = None, ): self.protocol = protocol self.port = port @@ -208,7 +289,12 @@ class LoadBalancerHealtCheckHttp(BaseDomain): """ def __init__( - self, domain=None, path=None, response=None, status_codes=None, tls=None + self, + domain: str | None = None, + path: str | None = None, + response: str | None = None, + status_codes: list | None = None, + tls: bool | None = None, ): self.domain = domain self.path = path @@ -233,7 +319,12 @@ class LoadBalancerTarget(BaseDomain): """ def __init__( - self, type=None, server=None, label_selector=None, ip=None, use_private_ip=None + self, + type: str | None = None, + server: BoundServer | None = None, + label_selector: LoadBalancerTargetLabelSelector | None = None, + ip: LoadBalancerTargetIP | None = None, + use_private_ip: bool | None = None, ): self.type = type self.server = server @@ -241,6 +332,30 @@ class LoadBalancerTarget(BaseDomain): self.ip = ip self.use_private_ip = use_private_ip + def to_payload(self) -> dict[str, Any]: + payload: dict[str, Any] = { + "type": self.type, + } + if self.use_private_ip is not None: + payload["use_private_ip"] = self.use_private_ip + + if self.type == "server": + if self.server is None: + raise ValueError(f"server is not defined in target {self!r}") + payload["server"] = {"id": self.server.id} + + elif self.type == "label_selector": + if self.label_selector is None: + raise ValueError(f"label_selector is not defined in target {self!r}") + payload["label_selector"] = {"selector": self.label_selector.selector} + + elif self.type == "ip": + if self.ip is None: + raise ValueError(f"ip is not defined in target {self!r}") + payload["ip"] = {"ip": self.ip.ip} + + return payload + class LoadBalancerTargetLabelSelector(BaseDomain): """LoadBalancerTargetLabelSelector Domain @@ -248,7 +363,7 @@ class LoadBalancerTargetLabelSelector(BaseDomain): :param selector: str Target label selector """ - def __init__(self, selector=None): + def __init__(self, selector: str | None = None): self.selector = selector @@ -258,7 +373,7 @@ class LoadBalancerTargetIP(BaseDomain): :param ip: str Target IP """ - def __init__(self, ip=None): + def __init__(self, ip: str | None = None): self.ip = ip @@ -269,7 +384,7 @@ class LoadBalancerAlgorithm(BaseDomain): Algorithm of the Load Balancer. Choices: round_robin, least_connections """ - def __init__(self, type=None): + def __init__(self, type: str | None = None): self.type = type @@ -285,9 +400,9 @@ class PublicNetwork(BaseDomain): def __init__( self, - ipv4, # type: IPv4Address - ipv6, # type: IPv6Network - enabled, # type: bool + ipv4: IPv4Address, + ipv6: IPv6Network, + enabled: bool, ): self.ipv4 = ipv4 self.ipv6 = ipv6 @@ -305,8 +420,8 @@ class IPv4Address(BaseDomain): def __init__( self, - ip, # type: str - dns_ptr, # type: str + ip: str, + dns_ptr: str, ): self.ip = ip self.dns_ptr = dns_ptr @@ -323,8 +438,8 @@ class IPv6Network(BaseDomain): def __init__( self, - ip, # type: str - dns_ptr, # type: str + ip: str, + dns_ptr: str, ): self.ip = ip self.dns_ptr = dns_ptr @@ -343,8 +458,8 @@ class PrivateNet(BaseDomain): def __init__( self, - network, # type: BoundNetwork - ip, # type: str + network: BoundNetwork, + ip: str, ): self.network = network self.ip = ip @@ -363,8 +478,8 @@ class CreateLoadBalancerResponse(BaseDomain): def __init__( self, - load_balancer, # type: BoundLoadBalancer - action, # type: BoundAction + load_balancer: BoundLoadBalancer, + action: BoundAction, ): self.load_balancer = load_balancer self.action = action diff --git a/plugins/module_utils/vendor/hcloud/locations/__init__.py b/plugins/module_utils/vendor/hcloud/locations/__init__.py index e69de29..1c23a51 100644 --- a/plugins/module_utils/vendor/hcloud/locations/__init__.py +++ b/plugins/module_utils/vendor/hcloud/locations/__init__.py @@ -0,0 +1,4 @@ +from __future__ import annotations + +from .client import BoundLocation, LocationsClient, LocationsPageResult # noqa: F401 +from .domain import Location # noqa: F401 diff --git a/plugins/module_utils/vendor/hcloud/locations/client.py b/plugins/module_utils/vendor/hcloud/locations/client.py index 6171ed6..2e2b6b4 100644 --- a/plugins/module_utils/vendor/hcloud/locations/client.py +++ b/plugins/module_utils/vendor/hcloud/locations/client.py @@ -1,16 +1,29 @@ -from ..core.client import BoundModelBase, ClientEntityBase, GetEntityByNameMixin +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..core import BoundModelBase, ClientEntityBase, Meta from .domain import Location +if TYPE_CHECKING: + from .._client import Client + class BoundLocation(BoundModelBase): + _client: LocationsClient + model = Location -class LocationsClient(ClientEntityBase, GetEntityByNameMixin): - results_list_attribute_name = "locations" +class LocationsPageResult(NamedTuple): + locations: list[BoundLocation] + meta: Meta | None - def get_by_id(self, id): - # type: (int) -> locations.client.BoundLocation + +class LocationsClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundLocation: """Get a specific location by its ID. :param id: int @@ -19,8 +32,12 @@ class LocationsClient(ClientEntityBase, GetEntityByNameMixin): response = self._client.request(url=f"/locations/{id}", method="GET") return BoundLocation(self, response["location"]) - def get_list(self, name=None, page=None, per_page=None): - # type: (Optional[str], Optional[int], Optional[int]) -> PageResult[List[BoundLocation], Meta] + def get_list( + self, + name: str | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> LocationsPageResult: """Get a list of locations :param name: str (optional) @@ -31,7 +48,7 @@ class LocationsClient(ClientEntityBase, GetEntityByNameMixin): Specifies how many results are returned by page :return: (List[:class:`BoundLocation `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if name is not None: params["name"] = name if page is not None: @@ -44,24 +61,22 @@ class LocationsClient(ClientEntityBase, GetEntityByNameMixin): BoundLocation(self, location_data) for location_data in response["locations"] ] - return self._add_meta_to_result(locations, response) + return LocationsPageResult(locations, Meta.parse_meta(response)) - def get_all(self, name=None): - # type: (Optional[str]) -> List[BoundLocation] + def get_all(self, name: str | None = None) -> list[BoundLocation]: """Get all locations :param name: str (optional) Can be used to filter locations by their name. :return: List[:class:`BoundLocation `] """ - return super().get_all(name=name) + return self._iter_pages(self.get_list, name=name) - def get_by_name(self, name): - # type: (str) -> BoundLocation + def get_by_name(self, name: str) -> BoundLocation | None: """Get location by name :param name: str Used to get location by name. :return: :class:`BoundLocation ` """ - return super().get_by_name(name) + return self._get_first_by(name=name) diff --git a/plugins/module_utils/vendor/hcloud/locations/domain.py b/plugins/module_utils/vendor/hcloud/locations/domain.py index 6ca16d6..7d4af22 100644 --- a/plugins/module_utils/vendor/hcloud/locations/domain.py +++ b/plugins/module_utils/vendor/hcloud/locations/domain.py @@ -1,4 +1,6 @@ -from ..core.domain import BaseDomain, DomainIdentityMixin +from __future__ import annotations + +from ..core import BaseDomain, DomainIdentityMixin class Location(BaseDomain, DomainIdentityMixin): @@ -35,14 +37,14 @@ class Location(BaseDomain, DomainIdentityMixin): def __init__( self, - id=None, - name=None, - description=None, - country=None, - city=None, - latitude=None, - longitude=None, - network_zone=None, + id: int | None = None, + name: str | None = None, + description: str | None = None, + country: str | None = None, + city: str | None = None, + latitude: float | None = None, + longitude: float | None = None, + network_zone: str | None = None, ): self.id = id self.name = name diff --git a/plugins/module_utils/vendor/hcloud/networks/__init__.py b/plugins/module_utils/vendor/hcloud/networks/__init__.py index e69de29..5bf4a88 100644 --- a/plugins/module_utils/vendor/hcloud/networks/__init__.py +++ b/plugins/module_utils/vendor/hcloud/networks/__init__.py @@ -0,0 +1,9 @@ +from __future__ import annotations + +from .client import BoundNetwork, NetworksClient, NetworksPageResult # noqa: F401 +from .domain import ( # noqa: F401 + CreateNetworkResponse, + Network, + NetworkRoute, + NetworkSubnet, +) diff --git a/plugins/module_utils/vendor/hcloud/networks/client.py b/plugins/module_utils/vendor/hcloud/networks/client.py index 1715205..416a040 100644 --- a/plugins/module_utils/vendor/hcloud/networks/client.py +++ b/plugins/module_utils/vendor/hcloud/networks/client.py @@ -1,13 +1,21 @@ -from ..actions.client import BoundAction -from ..core.client import BoundModelBase, ClientEntityBase, GetEntityByNameMixin -from ..core.domain import add_meta_to_result +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..actions import ActionsPageResult, BoundAction +from ..core import BoundModelBase, ClientEntityBase, Meta from .domain import Network, NetworkRoute, NetworkSubnet +if TYPE_CHECKING: + from .._client import Client + class BoundNetwork(BoundModelBase): + _client: NetworksClient + model = Network - def __init__(self, client, data, complete=True): + def __init__(self, client: NetworksClient, data: dict, complete: bool = True): subnets = data.get("subnets", []) if subnets is not None: subnets = [NetworkSubnet.from_dict(subnet) for subnet in subnets] @@ -18,7 +26,7 @@ class BoundNetwork(BoundModelBase): routes = [NetworkRoute.from_dict(route) for route in routes] data["routes"] = routes - from ..servers.client import BoundServer + from ..servers import BoundServer servers = data.get("servers", []) if servers is not None: @@ -32,10 +40,10 @@ class BoundNetwork(BoundModelBase): def update( self, - name=None, # type: Optional[str] - expose_routes_to_vswitch=None, # type: Optional[bool] - labels=None, # type: Optional[Dict[str, str]] - ): # type: (...) -> BoundNetwork + name: str | None = None, + expose_routes_to_vswitch: bool | None = None, + labels: dict[str, str] | None = None, + ) -> BoundNetwork: """Updates a network. You can update a network’s name and a networks’s labels. :param name: str (optional) @@ -54,16 +62,20 @@ class BoundNetwork(BoundModelBase): labels=labels, ) - def delete(self): - # type: () -> BoundAction + def delete(self) -> bool: """Deletes a network. :return: boolean """ return self._client.delete(self) - def get_actions_list(self, status=None, sort=None, page=None, per_page=None): - # type: (Optional[List[str]], Optional[List[str]], Optional[int], Optional[int]) -> PageResults[List[BoundAction, Meta]] + def get_actions_list( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: """Returns all action objects for a network. :param status: List[str] (optional) @@ -78,8 +90,11 @@ class BoundNetwork(BoundModelBase): """ return self._client.get_actions_list(self, status, sort, page, per_page) - def get_actions(self, status=None, sort=None): - # type: (Optional[List[str]], Optional[List[str]]) -> List[BoundAction] + def get_actions( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: """Returns all action objects for a network. :param status: List[str] (optional) @@ -90,8 +105,7 @@ class BoundNetwork(BoundModelBase): """ return self._client.get_actions(self, status, sort) - def add_subnet(self, subnet): - # type: (NetworkSubnet) -> List[BoundAction] + def add_subnet(self, subnet: NetworkSubnet) -> BoundAction: """Adds a subnet entry to a network. :param subnet: :class:`NetworkSubnet ` @@ -100,8 +114,7 @@ class BoundNetwork(BoundModelBase): """ return self._client.add_subnet(self, subnet=subnet) - def delete_subnet(self, subnet): - # type: (NetworkSubnet) -> List[BoundAction] + def delete_subnet(self, subnet: NetworkSubnet) -> BoundAction: """Removes a subnet entry from a network :param subnet: :class:`NetworkSubnet ` @@ -110,8 +123,7 @@ class BoundNetwork(BoundModelBase): """ return self._client.delete_subnet(self, subnet=subnet) - def add_route(self, route): - # type: (NetworkRoute) -> List[BoundAction] + def add_route(self, route: NetworkRoute) -> BoundAction: """Adds a route entry to a network. :param route: :class:`NetworkRoute ` @@ -120,8 +132,7 @@ class BoundNetwork(BoundModelBase): """ return self._client.add_route(self, route=route) - def delete_route(self, route): - # type: (NetworkRoute) -> List[BoundAction] + def delete_route(self, route: NetworkRoute) -> BoundAction: """Removes a route entry to a network. :param route: :class:`NetworkRoute ` @@ -130,8 +141,7 @@ class BoundNetwork(BoundModelBase): """ return self._client.delete_route(self, route=route) - def change_ip_range(self, ip_range): - # type: (str) -> List[BoundAction] + def change_ip_range(self, ip_range: str) -> BoundAction: """Changes the IP range of a network. :param ip_range: str @@ -140,8 +150,7 @@ class BoundNetwork(BoundModelBase): """ return self._client.change_ip_range(self, ip_range=ip_range) - def change_protection(self, delete=None): - # type: (Optional[bool]) -> BoundAction + def change_protection(self, delete: bool | None = None) -> BoundAction: """Changes the protection configuration of a network. :param delete: boolean @@ -151,11 +160,15 @@ class BoundNetwork(BoundModelBase): return self._client.change_protection(self, delete=delete) -class NetworksClient(ClientEntityBase, GetEntityByNameMixin): - results_list_attribute_name = "networks" +class NetworksPageResult(NamedTuple): + networks: list[BoundNetwork] + meta: Meta | None - def get_by_id(self, id): - # type: (int) -> BoundNetwork + +class NetworksClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundNetwork: """Get a specific network :param id: int @@ -166,12 +179,11 @@ class NetworksClient(ClientEntityBase, GetEntityByNameMixin): def get_list( self, - name=None, # type: Optional[str] - label_selector=None, # type: Optional[str] - page=None, # type: Optional[int] - per_page=None, # type: Optional[int] - ): - # type: (...) -> PageResults[List[BoundNetwork], Meta] + name: str | None = None, + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> NetworksPageResult: """Get a list of networks from this account :param name: str (optional) @@ -184,7 +196,7 @@ class NetworksClient(ClientEntityBase, GetEntityByNameMixin): Specifies how many results are returned by page :return: (List[:class:`BoundNetwork `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if name is not None: params["name"] = name if label_selector is not None: @@ -196,13 +208,16 @@ class NetworksClient(ClientEntityBase, GetEntityByNameMixin): response = self._client.request(url="/networks", method="GET", params=params) - ass_networks = [ + networks = [ BoundNetwork(self, network_data) for network_data in response["networks"] ] - return self._add_meta_to_result(ass_networks, response) + return NetworksPageResult(networks, Meta.parse_meta(response)) - def get_all(self, name=None, label_selector=None): - # type: (Optional[str], Optional[str]) -> List[BoundNetwork] + def get_all( + self, + name: str | None = None, + label_selector: str | None = None, + ) -> list[BoundNetwork]: """Get all networks from this account :param name: str (optional) @@ -211,27 +226,26 @@ class NetworksClient(ClientEntityBase, GetEntityByNameMixin): Can be used to filter networks by labels. The response will only contain networks matching the label selector. :return: List[:class:`BoundNetwork `] """ - return super().get_all(name=name, label_selector=label_selector) + return self._iter_pages(self.get_list, name=name, label_selector=label_selector) - def get_by_name(self, name): - # type: (str) -> BoundNetwork + def get_by_name(self, name: str) -> BoundNetwork | None: """Get network by name :param name: str Used to get network by name. :return: :class:`BoundNetwork ` """ - return super().get_by_name(name) + return self._get_first_by(name=name) def create( self, - name, # type: str - ip_range, # type: str - subnets=None, # type: Optional[List[NetworkSubnet]] - routes=None, # type: Optional[List[NetworkRoute]] - expose_routes_to_vswitch=None, # type: Optional[bool] - labels=None, # type: Optional[Dict[str, str]] - ): + name: str, + ip_range: str, + subnets: list[NetworkSubnet] | None = None, + routes: list[NetworkRoute] | None = None, + expose_routes_to_vswitch: bool | None = None, + labels: dict[str, str] | None = None, + ) -> BoundNetwork: """Creates a network with range ip_range. :param name: str @@ -249,11 +263,11 @@ class NetworksClient(ClientEntityBase, GetEntityByNameMixin): User-defined labels (key-value pairs) :return: :class:`BoundNetwork ` """ - data = {"name": name, "ip_range": ip_range} + data: dict[str, Any] = {"name": name, "ip_range": ip_range} if subnets is not None: data_subnets = [] for subnet in subnets: - data_subnet = { + data_subnet: dict[str, Any] = { "type": subnet.type, "ip_range": subnet.ip_range, "network_zone": subnet.network_zone, @@ -280,8 +294,13 @@ class NetworksClient(ClientEntityBase, GetEntityByNameMixin): return BoundNetwork(self, response["network"]) - def update(self, network, name=None, expose_routes_to_vswitch=None, labels=None): - # type:(Network, Optional[str], Optional[bool], Optional[Dict[str, str]]) -> BoundNetwork + def update( + self, + network: Network | BoundNetwork, + name: str | None = None, + expose_routes_to_vswitch: bool | None = None, + labels: dict[str, str] | None = None, + ) -> BoundNetwork: """Updates a network. You can update a network’s name and a network’s labels. :param network: :class:`BoundNetwork ` or :class:`Network ` @@ -294,7 +313,7 @@ class NetworksClient(ClientEntityBase, GetEntityByNameMixin): User-defined labels (key-value pairs) :return: :class:`BoundNetwork ` """ - data = {} + data: dict[str, Any] = {} if name is not None: data.update({"name": name}) @@ -311,8 +330,7 @@ class NetworksClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundNetwork(self, response["network"]) - def delete(self, network): - # type: (Network) -> BoundAction + def delete(self, network: Network | BoundNetwork) -> bool: """Deletes a network. :param network: :class:`BoundNetwork ` or :class:`Network ` @@ -322,9 +340,13 @@ class NetworksClient(ClientEntityBase, GetEntityByNameMixin): return True def get_actions_list( - self, network, status=None, sort=None, page=None, per_page=None - ): - # type: (Network, Optional[List[str]], Optional[List[str]], Optional[int], Optional[int]) -> PageResults[List[BoundAction], Meta] + self, + network: Network | BoundNetwork, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: """Returns all action objects for a network. :param network: :class:`BoundNetwork ` or :class:`Network ` @@ -338,7 +360,7 @@ class NetworksClient(ClientEntityBase, GetEntityByNameMixin): Specifies how many results are returned by page :return: (List[:class:`BoundAction `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if status is not None: params["status"] = status if sort is not None: @@ -357,10 +379,14 @@ class NetworksClient(ClientEntityBase, GetEntityByNameMixin): BoundAction(self._client.actions, action_data) for action_data in response["actions"] ] - return add_meta_to_result(actions, response, "actions") + return ActionsPageResult(actions, Meta.parse_meta(response)) - def get_actions(self, network, status=None, sort=None): - # type: (Network, Optional[List[str]], Optional[List[str]]) -> List[BoundAction] + def get_actions( + self, + network: Network | BoundNetwork, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: """Returns all action objects for a network. :param network: :class:`BoundNetwork ` or :class:`Network ` @@ -370,10 +396,18 @@ class NetworksClient(ClientEntityBase, GetEntityByNameMixin): Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` :return: List[:class:`BoundAction `] """ - return super().get_actions(network, status=status, sort=sort) + return self._iter_pages( + self.get_actions_list, + network, + status=status, + sort=sort, + ) - def add_subnet(self, network, subnet): - # type: (Union[Network, BoundNetwork], NetworkSubnet) -> List[BoundAction] + def add_subnet( + self, + network: Network | BoundNetwork, + subnet: NetworkSubnet, + ) -> BoundAction: """Adds a subnet entry to a network. :param network: :class:`BoundNetwork ` or :class:`Network ` @@ -381,23 +415,27 @@ class NetworksClient(ClientEntityBase, GetEntityByNameMixin): The NetworkSubnet you want to add to the Network :return: :class:`BoundAction ` """ - data = {"type": subnet.type, "network_zone": subnet.network_zone} + data: dict[str, Any] = { + "type": subnet.type, + "network_zone": subnet.network_zone, + } if subnet.ip_range is not None: data["ip_range"] = subnet.ip_range if subnet.vswitch_id is not None: data["vswitch_id"] = subnet.vswitch_id response = self._client.request( - url="/networks/{network_id}/actions/add_subnet".format( - network_id=network.id - ), + url=f"/networks/{network.id}/actions/add_subnet", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def delete_subnet(self, network, subnet): - # type: (Union[Network, BoundNetwork], NetworkSubnet) -> List[BoundAction] + def delete_subnet( + self, + network: Network | BoundNetwork, + subnet: NetworkSubnet, + ) -> BoundAction: """Removes a subnet entry from a network :param network: :class:`BoundNetwork ` or :class:`Network ` @@ -405,19 +443,20 @@ class NetworksClient(ClientEntityBase, GetEntityByNameMixin): The NetworkSubnet you want to remove from the Network :return: :class:`BoundAction ` """ - data = {"ip_range": subnet.ip_range} + data: dict[str, Any] = {"ip_range": subnet.ip_range} response = self._client.request( - url="/networks/{network_id}/actions/delete_subnet".format( - network_id=network.id - ), + url=f"/networks/{network.id}/actions/delete_subnet", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def add_route(self, network, route): - # type: (Union[Network, BoundNetwork], NetworkRoute) -> List[BoundAction] + def add_route( + self, + network: Network | BoundNetwork, + route: NetworkRoute, + ) -> BoundAction: """Adds a route entry to a network. :param network: :class:`BoundNetwork ` or :class:`Network ` @@ -425,19 +464,23 @@ class NetworksClient(ClientEntityBase, GetEntityByNameMixin): The NetworkRoute you want to add to the Network :return: :class:`BoundAction ` """ - data = {"destination": route.destination, "gateway": route.gateway} + data: dict[str, Any] = { + "destination": route.destination, + "gateway": route.gateway, + } response = self._client.request( - url="/networks/{network_id}/actions/add_route".format( - network_id=network.id - ), + url=f"/networks/{network.id}/actions/add_route", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def delete_route(self, network, route): - # type: (Union[Network, BoundNetwork], NetworkRoute) -> List[BoundAction] + def delete_route( + self, + network: Network | BoundNetwork, + route: NetworkRoute, + ) -> BoundAction: """Removes a route entry to a network. :param network: :class:`BoundNetwork ` or :class:`Network ` @@ -445,19 +488,23 @@ class NetworksClient(ClientEntityBase, GetEntityByNameMixin): The NetworkRoute you want to remove from the Network :return: :class:`BoundAction ` """ - data = {"destination": route.destination, "gateway": route.gateway} + data: dict[str, Any] = { + "destination": route.destination, + "gateway": route.gateway, + } response = self._client.request( - url="/networks/{network_id}/actions/delete_route".format( - network_id=network.id - ), + url=f"/networks/{network.id}/actions/delete_route", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def change_ip_range(self, network, ip_range): - # type: (Union[Network, BoundNetwork], str) -> List[BoundAction] + def change_ip_range( + self, + network: Network | BoundNetwork, + ip_range: str, + ) -> BoundAction: """Changes the IP range of a network. :param network: :class:`BoundNetwork ` or :class:`Network ` @@ -465,19 +512,20 @@ class NetworksClient(ClientEntityBase, GetEntityByNameMixin): The new prefix for the whole network. :return: :class:`BoundAction ` """ - data = {"ip_range": ip_range} + data: dict[str, Any] = {"ip_range": ip_range} response = self._client.request( - url="/networks/{network_id}/actions/change_ip_range".format( - network_id=network.id - ), + url=f"/networks/{network.id}/actions/change_ip_range", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def change_protection(self, network, delete=None): - # type: (Union[Network, BoundNetwork], Optional[bool]) -> BoundAction + def change_protection( + self, + network: Network | BoundNetwork, + delete: bool | None = None, + ) -> BoundAction: """Changes the protection configuration of a network. :param network: :class:`BoundNetwork ` or :class:`Network ` @@ -485,14 +533,12 @@ class NetworksClient(ClientEntityBase, GetEntityByNameMixin): If True, prevents the network from being deleted :return: :class:`BoundAction ` """ - data = {} + data: dict[str, Any] = {} if delete is not None: data.update({"delete": delete}) response = self._client.request( - url="/networks/{network_id}/actions/change_protection".format( - network_id=network.id - ), + url=f"/networks/{network.id}/actions/change_protection", method="POST", json=data, ) diff --git a/plugins/module_utils/vendor/hcloud/networks/domain.py b/plugins/module_utils/vendor/hcloud/networks/domain.py index 32285a3..c307bf9 100644 --- a/plugins/module_utils/vendor/hcloud/networks/domain.py +++ b/plugins/module_utils/vendor/hcloud/networks/domain.py @@ -1,9 +1,18 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + try: from dateutil.parser import isoparse except ImportError: isoparse = None -from ..core.domain import BaseDomain +from ..core import BaseDomain + +if TYPE_CHECKING: + from ..actions import BoundAction + from ..servers import BoundServer + from .client import BoundNetwork class Network(BaseDomain): @@ -44,16 +53,16 @@ class Network(BaseDomain): def __init__( self, - id, - name=None, - created=None, - ip_range=None, - subnets=None, - routes=None, - expose_routes_to_vswitch=None, - servers=None, - protection=None, - labels=None, + id: int, + name: str | None = None, + created: str | None = None, + ip_range: str | None = None, + subnets: list[NetworkSubnet] | None = None, + routes: list[NetworkRoute] | None = None, + expose_routes_to_vswitch: bool | None = None, + servers: list[BoundServer] | None = None, + protection: dict | None = None, + labels: dict[str, str] | None = None, ): self.id = id self.name = name @@ -91,7 +100,12 @@ class NetworkSubnet(BaseDomain): __slots__ = ("type", "ip_range", "network_zone", "gateway", "vswitch_id") def __init__( - self, ip_range, type=None, network_zone=None, gateway=None, vswitch_id=None + self, + ip_range: str, + type: str | None = None, + network_zone: str | None = None, + gateway: str | None = None, + vswitch_id: int | None = None, ): self.type = type self.ip_range = ip_range @@ -111,7 +125,7 @@ class NetworkRoute(BaseDomain): __slots__ = ("destination", "gateway") - def __init__(self, destination, gateway): + def __init__(self, destination: str, gateway: str): self.destination = destination self.gateway = gateway @@ -129,8 +143,8 @@ class CreateNetworkResponse(BaseDomain): def __init__( self, - network, # type: BoundNetwork - action, # type: BoundAction + network: BoundNetwork, + action: BoundAction, ): self.network = network self.action = action diff --git a/plugins/module_utils/vendor/hcloud/placement_groups/__init__.py b/plugins/module_utils/vendor/hcloud/placement_groups/__init__.py index e69de29..9c25dd7 100644 --- a/plugins/module_utils/vendor/hcloud/placement_groups/__init__.py +++ b/plugins/module_utils/vendor/hcloud/placement_groups/__init__.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .client import ( # noqa: F401 + BoundPlacementGroup, + PlacementGroupsClient, + PlacementGroupsPageResult, +) +from .domain import CreatePlacementGroupResponse, PlacementGroup # noqa: F401 diff --git a/plugins/module_utils/vendor/hcloud/placement_groups/client.py b/plugins/module_utils/vendor/hcloud/placement_groups/client.py index c5cb7c0..b551904 100644 --- a/plugins/module_utils/vendor/hcloud/placement_groups/client.py +++ b/plugins/module_utils/vendor/hcloud/placement_groups/client.py @@ -1,13 +1,25 @@ -from ..actions.client import BoundAction -from ..core.client import BoundModelBase, ClientEntityBase, GetEntityByNameMixin +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..actions import BoundAction +from ..core import BoundModelBase, ClientEntityBase, Meta from .domain import CreatePlacementGroupResponse, PlacementGroup +if TYPE_CHECKING: + from .._client import Client + class BoundPlacementGroup(BoundModelBase): + _client: PlacementGroupsClient + model = PlacementGroup - def update(self, labels=None, name=None): - # type: (Optional[str], Optional[Dict[str, str]], Optional[str]) -> BoundPlacementGroup + def update( + self, + labels: dict[str, str] | None = None, + name: str | None = None, + ) -> BoundPlacementGroup: """Updates the name or labels of a Placement Group :param labels: Dict[str, str] (optional) @@ -18,8 +30,7 @@ class BoundPlacementGroup(BoundModelBase): """ return self._client.update(self, labels, name) - def delete(self): - # type: () -> bool + def delete(self) -> bool: """Deletes a Placement Group :return: boolean @@ -27,11 +38,15 @@ class BoundPlacementGroup(BoundModelBase): return self._client.delete(self) -class PlacementGroupsClient(ClientEntityBase, GetEntityByNameMixin): - results_list_attribute_name = "placement_groups" +class PlacementGroupsPageResult(NamedTuple): + placement_groups: list[BoundPlacementGroup] + meta: Meta | None - def get_by_id(self, id): - # type: (int) -> BoundPlacementGroup + +class PlacementGroupsClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundPlacementGroup: """Returns a specific Placement Group object :param id: int @@ -45,14 +60,13 @@ class PlacementGroupsClient(ClientEntityBase, GetEntityByNameMixin): def get_list( self, - label_selector=None, # type: Optional[str] - page=None, # type: Optional[int] - per_page=None, # type: Optional[int] - name=None, # type: Optional[str] - sort=None, # type: Optional[List[str]] - type=None, # type: Optional[str] - ): - # type: (...) -> PageResults[List[BoundPlacementGroup]] + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + name: str | None = None, + sort: list[str] | None = None, + type: str | None = None, + ) -> PlacementGroupsPageResult: """Get a list of Placement Groups :param label_selector: str (optional) @@ -68,7 +82,7 @@ class PlacementGroupsClient(ClientEntityBase, GetEntityByNameMixin): :return: (List[:class:`BoundPlacementGroup `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if label_selector is not None: params["label_selector"] = label_selector @@ -90,10 +104,14 @@ class PlacementGroupsClient(ClientEntityBase, GetEntityByNameMixin): for placement_group_data in response["placement_groups"] ] - return self._add_meta_to_result(placement_groups, response) + return PlacementGroupsPageResult(placement_groups, Meta.parse_meta(response)) - def get_all(self, label_selector=None, name=None, sort=None): - # type: (Optional[str], Optional[str], Optional[List[str]]) -> List[BoundPlacementGroup] + def get_all( + self, + label_selector: str | None = None, + name: str | None = None, + sort: list[str] | None = None, + ) -> list[BoundPlacementGroup]: """Get all Placement Groups :param label_selector: str (optional) @@ -104,25 +122,28 @@ class PlacementGroupsClient(ClientEntityBase, GetEntityByNameMixin): Choices: id name created (You can add one of ":asc", ":desc" to modify sort order. ( ":asc" is default)) :return: List[:class:`BoundPlacementGroup `] """ - return super().get_all(label_selector=label_selector, name=name, sort=sort) + return self._iter_pages( + self.get_list, + label_selector=label_selector, + name=name, + sort=sort, + ) - def get_by_name(self, name): - # type: (str) -> BoundPlacementGroup + def get_by_name(self, name: str) -> BoundPlacementGroup | None: """Get Placement Group by name :param name: str Used to get Placement Group by name :return: class:`BoundPlacementGroup ` """ - return super().get_by_name(name) + return self._get_first_by(name=name) def create( self, - name, # type: str - type, # type: str - labels=None, # type: Optional[Dict[str, str]] - ): - # type: (...) -> CreatePlacementGroupResponse + name: str, + type: str, + labels: dict[str, str] | None = None, + ) -> CreatePlacementGroupResponse: """Creates a new Placement Group. :param name: str @@ -134,7 +155,7 @@ class PlacementGroupsClient(ClientEntityBase, GetEntityByNameMixin): :return: :class:`CreatePlacementGroupResponse ` """ - data = {"name": name, "type": type} + data: dict[str, Any] = {"name": name, "type": type} if labels is not None: data["labels"] = labels response = self._client.request( @@ -143,7 +164,7 @@ class PlacementGroupsClient(ClientEntityBase, GetEntityByNameMixin): action = None if response.get("action") is not None: - action = BoundAction(self._client.action, response["action"]) + action = BoundAction(self._client.actions, response["action"]) result = CreatePlacementGroupResponse( placement_group=BoundPlacementGroup(self, response["placement_group"]), @@ -151,8 +172,12 @@ class PlacementGroupsClient(ClientEntityBase, GetEntityByNameMixin): ) return result - def update(self, placement_group, labels=None, name=None): - # type: (PlacementGroup, Optional[Dict[str, str]], Optional[str]) -> BoundPlacementGroup + def update( + self, + placement_group: PlacementGroup | BoundPlacementGroup, + labels: dict[str, str] | None = None, + name: str | None = None, + ) -> BoundPlacementGroup: """Updates the description or labels of a Placement Group. :param placement_group: :class:`BoundPlacementGroup ` or :class:`PlacementGroup ` @@ -163,32 +188,27 @@ class PlacementGroupsClient(ClientEntityBase, GetEntityByNameMixin): :return: :class:`BoundPlacementGroup ` """ - data = {} + data: dict[str, Any] = {} if labels is not None: data["labels"] = labels if name is not None: data["name"] = name response = self._client.request( - url="/placement_groups/{placement_group_id}".format( - placement_group_id=placement_group.id - ), + url=f"/placement_groups/{placement_group.id}", method="PUT", json=data, ) return BoundPlacementGroup(self, response["placement_group"]) - def delete(self, placement_group): - # type: (PlacementGroup) -> bool + def delete(self, placement_group: PlacementGroup | BoundPlacementGroup) -> bool: """Deletes a Placement Group. :param placement_group: :class:`BoundPlacementGroup ` or :class:`PlacementGroup ` :return: boolean """ self._client.request( - url="/placement_groups/{placement_group_id}".format( - placement_group_id=placement_group.id - ), + url=f"/placement_groups/{placement_group.id}", method="DELETE", ) return True diff --git a/plugins/module_utils/vendor/hcloud/placement_groups/domain.py b/plugins/module_utils/vendor/hcloud/placement_groups/domain.py index e02443c..16b2a39 100644 --- a/plugins/module_utils/vendor/hcloud/placement_groups/domain.py +++ b/plugins/module_utils/vendor/hcloud/placement_groups/domain.py @@ -1,9 +1,17 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + try: from dateutil.parser import isoparse except ImportError: isoparse = None -from ..core.domain import BaseDomain +from ..core import BaseDomain + +if TYPE_CHECKING: + from ..actions import BoundAction + from .client import BoundPlacementGroup class PlacementGroup(BaseDomain): @@ -31,7 +39,13 @@ class PlacementGroup(BaseDomain): TYPE_SPREAD = "spread" def __init__( - self, id=None, name=None, labels=None, servers=None, type=None, created=None + self, + id: int | None = None, + name: str | None = None, + labels: dict[str, str] | None = None, + servers: list[int] | None = None, + type: str | None = None, + created: str | None = None, ): self.id = id self.name = name @@ -54,8 +68,8 @@ class CreatePlacementGroupResponse(BaseDomain): def __init__( self, - placement_group, # type: BoundPlacementGroup - action, # type: BoundAction + placement_group: BoundPlacementGroup, + action: BoundAction | None, ): self.placement_group = placement_group self.action = action diff --git a/plugins/module_utils/vendor/hcloud/primary_ips/__init__.py b/plugins/module_utils/vendor/hcloud/primary_ips/__init__.py index e69de29..d079a23 100644 --- a/plugins/module_utils/vendor/hcloud/primary_ips/__init__.py +++ b/plugins/module_utils/vendor/hcloud/primary_ips/__init__.py @@ -0,0 +1,4 @@ +from __future__ import annotations + +from .client import BoundPrimaryIP, PrimaryIPsClient, PrimaryIPsPageResult # noqa: F401 +from .domain import CreatePrimaryIPResponse, PrimaryIP # noqa: F401 diff --git a/plugins/module_utils/vendor/hcloud/primary_ips/client.py b/plugins/module_utils/vendor/hcloud/primary_ips/client.py index e8c8d67..a54de6d 100644 --- a/plugins/module_utils/vendor/hcloud/primary_ips/client.py +++ b/plugins/module_utils/vendor/hcloud/primary_ips/client.py @@ -1,13 +1,23 @@ -from ..actions.client import BoundAction -from ..core.client import BoundModelBase, ClientEntityBase, GetEntityByNameMixin +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..actions import BoundAction +from ..core import BoundModelBase, ClientEntityBase, Meta from .domain import CreatePrimaryIPResponse, PrimaryIP +if TYPE_CHECKING: + from .._client import Client + from ..datacenters import BoundDatacenter, Datacenter + class BoundPrimaryIP(BoundModelBase): + _client: PrimaryIPsClient + model = PrimaryIP - def __init__(self, client, data, complete=True): - from ..datacenters.client import BoundDatacenter + def __init__(self, client: PrimaryIPsClient, data: dict, complete: bool = True): + from ..datacenters import BoundDatacenter datacenter = data.get("datacenter", {}) if datacenter: @@ -15,8 +25,12 @@ class BoundPrimaryIP(BoundModelBase): super().__init__(client, data, complete) - def update(self, auto_delete=None, labels=None, name=None): - # type: (Optional[bool], Optional[Dict[str, str]], Optional[str]) -> BoundPrimaryIP + def update( + self, + auto_delete: bool | None = None, + labels: dict[str, str] | None = None, + name: str | None = None, + ) -> BoundPrimaryIP: """Updates the description or labels of a Primary IP. :param auto_delete: bool (optional) @@ -31,16 +45,14 @@ class BoundPrimaryIP(BoundModelBase): self, auto_delete=auto_delete, labels=labels, name=name ) - def delete(self): - # type: () -> bool + def delete(self) -> bool: """Deletes a Primary IP. If it is currently assigned to a server it will automatically get unassigned. :return: boolean """ return self._client.delete(self) - def change_protection(self, delete=None): - # type: (Optional[bool]) -> BoundAction + def change_protection(self, delete: bool | None = None) -> BoundAction: """Changes the protection configuration of the Primary IP. :param delete: boolean @@ -49,8 +61,7 @@ class BoundPrimaryIP(BoundModelBase): """ return self._client.change_protection(self, delete) - def assign(self, assignee_id, assignee_type): - # type: (int,str) -> BoundAction + def assign(self, assignee_id: int, assignee_type: str) -> BoundAction: """Assigns a Primary IP to a assignee. :param assignee_id: int` @@ -61,16 +72,14 @@ class BoundPrimaryIP(BoundModelBase): """ return self._client.assign(self, assignee_id, assignee_type) - def unassign(self): - # type: () -> BoundAction + def unassign(self) -> BoundAction: """Unassigns a Primary IP, resulting in it being unreachable. You may assign it to a server again at a later time. :return: :class:`BoundAction ` """ return self._client.unassign(self) - def change_dns_ptr(self, ip, dns_ptr): - # type: (str, str) -> BoundAction + def change_dns_ptr(self, ip: str, dns_ptr: str) -> BoundAction: """Changes the hostname that will appear when getting the hostname belonging to this Primary IP. :param ip: str @@ -82,11 +91,15 @@ class BoundPrimaryIP(BoundModelBase): return self._client.change_dns_ptr(self, ip, dns_ptr) -class PrimaryIPsClient(ClientEntityBase, GetEntityByNameMixin): - results_list_attribute_name = "primary_ips" +class PrimaryIPsPageResult(NamedTuple): + primary_ips: list[BoundPrimaryIP] + meta: Meta | None - def get_by_id(self, id): - # type: (int) -> BoundPrimaryIP + +class PrimaryIPsClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundPrimaryIP: """Returns a specific Primary IP object. :param id: int @@ -97,13 +110,12 @@ class PrimaryIPsClient(ClientEntityBase, GetEntityByNameMixin): def get_list( self, - label_selector=None, # type: Optional[str] - page=None, # type: Optional[int] - per_page=None, # type: Optional[int] - name=None, # type: Optional[str] - ip=None, # type: Optional[ip] - ): - # type: (...) -> PageResults[List[BoundPrimaryIP]] + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + name: str | None = None, + ip: str | None = None, + ) -> PrimaryIPsPageResult: """Get a list of primary ips from this account :param label_selector: str (optional) @@ -118,7 +130,7 @@ class PrimaryIPsClient(ClientEntityBase, GetEntityByNameMixin): Can be used to filter resources by their ip. The response will only contain the resources matching the specified ip. :return: (List[:class:`BoundPrimaryIP `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if label_selector is not None: params["label_selector"] = label_selector @@ -137,10 +149,13 @@ class PrimaryIPsClient(ClientEntityBase, GetEntityByNameMixin): for primary_ip_data in response["primary_ips"] ] - return self._add_meta_to_result(primary_ips, response) + return PrimaryIPsPageResult(primary_ips, Meta.parse_meta(response)) - def get_all(self, label_selector=None, name=None): - # type: (Optional[str], Optional[str]) -> List[BoundPrimaryIP] + def get_all( + self, + label_selector: str | None = None, + name: str | None = None, + ) -> list[BoundPrimaryIP]: """Get all primary ips from this account :param label_selector: str (optional) @@ -149,29 +164,28 @@ class PrimaryIPsClient(ClientEntityBase, GetEntityByNameMixin): Can be used to filter networks by their name. :return: List[:class:`BoundPrimaryIP `] """ - return super().get_all(label_selector=label_selector, name=name) + return self._iter_pages(self.get_list, label_selector=label_selector, name=name) - def get_by_name(self, name): - # type: (str) -> BoundPrimaryIP + def get_by_name(self, name: str) -> BoundPrimaryIP | None: """Get Primary IP by name :param name: str Used to get Primary IP by name. :return: :class:`BoundPrimaryIP ` """ - return super().get_by_name(name) + return self._get_first_by(name=name) def create( self, - type, # type: str - datacenter, # type: Datacenter - name, # type: str - assignee_type="server", # type: Optional[str] - assignee_id=None, # type: Optional[int] - auto_delete=False, # type: Optional[bool] - labels=None, # type: Optional[dict] - ): - # type: (...) -> CreatePrimaryIPResponse + type: str, + # TODO: Make the datacenter argument optional + datacenter: Datacenter | BoundDatacenter | None, + name: str, + assignee_type: str | None = "server", + assignee_id: int | None = None, + auto_delete: bool | None = False, + labels: dict | None = None, + ) -> CreatePrimaryIPResponse: """Creates a new Primary IP assigned to a server. :param type: str @@ -186,14 +200,15 @@ class PrimaryIPsClient(ClientEntityBase, GetEntityByNameMixin): :return: :class:`CreatePrimaryIPResponse ` """ - data = { + data: dict[str, Any] = { "type": type, "assignee_type": assignee_type, "auto_delete": auto_delete, - "datacenter": datacenter.id_or_name, "name": name, } - if assignee_id: + if datacenter is not None: + data["datacenter"] = datacenter.id_or_name + if assignee_id is not None: data["assignee_id"] = assignee_id if labels is not None: data["labels"] = labels @@ -209,8 +224,13 @@ class PrimaryIPsClient(ClientEntityBase, GetEntityByNameMixin): ) return result - def update(self, primary_ip, auto_delete=None, labels=None, name=None): - # type: (PrimaryIP, Optional[bool], Optional[Dict[str, str]], Optional[str]) -> BoundPrimaryIP + def update( + self, + primary_ip: PrimaryIP | BoundPrimaryIP, + auto_delete: bool | None = None, + labels: dict[str, str] | None = None, + name: str | None = None, + ) -> BoundPrimaryIP: """Updates the name, auto_delete or labels of a Primary IP. :param primary_ip: :class:`BoundPrimaryIP ` or :class:`PrimaryIP ` @@ -222,7 +242,7 @@ class PrimaryIPsClient(ClientEntityBase, GetEntityByNameMixin): New name to set :return: :class:`BoundPrimaryIP ` """ - data = {} + data: dict[str, Any] = {} if auto_delete is not None: data["auto_delete"] = auto_delete if labels is not None: @@ -237,8 +257,7 @@ class PrimaryIPsClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundPrimaryIP(self, response["primary_ip"]) - def delete(self, primary_ip): - # type: (PrimaryIP) -> bool + def delete(self, primary_ip: PrimaryIP | BoundPrimaryIP) -> bool: """Deletes a Primary IP. If it is currently assigned to an assignee it will automatically get unassigned. :param primary_ip: :class:`BoundPrimaryIP ` or :class:`PrimaryIP ` @@ -251,8 +270,11 @@ class PrimaryIPsClient(ClientEntityBase, GetEntityByNameMixin): # Return always true, because the API does not return an action for it. When an error occurs a HcloudAPIException will be raised return True - def change_protection(self, primary_ip, delete=None): - # type: (PrimaryIP, Optional[bool]) -> BoundAction + def change_protection( + self, + primary_ip: PrimaryIP | BoundPrimaryIP, + delete: bool | None = None, + ) -> BoundAction: """Changes the protection configuration of the Primary IP. :param primary_ip: :class:`BoundPrimaryIP ` or :class:`PrimaryIP ` @@ -260,21 +282,23 @@ class PrimaryIPsClient(ClientEntityBase, GetEntityByNameMixin): If true, prevents the Primary IP from being deleted :return: :class:`BoundAction ` """ - data = {} + data: dict[str, Any] = {} if delete is not None: data.update({"delete": delete}) response = self._client.request( - url="/primary_ips/{primary_ip_id}/actions/change_protection".format( - primary_ip_id=primary_ip.id - ), + url=f"/primary_ips/{primary_ip.id}/actions/change_protection", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def assign(self, primary_ip, assignee_id, assignee_type="server"): - # type: (PrimaryIP, int, str) -> BoundAction + def assign( + self, + primary_ip: PrimaryIP | BoundPrimaryIP, + assignee_id: int, + assignee_type: str = "server", + ) -> BoundAction: """Assigns a Primary IP to a assignee_id. :param primary_ip: :class:`BoundPrimaryIP ` or :class:`PrimaryIP ` @@ -285,31 +309,30 @@ class PrimaryIPsClient(ClientEntityBase, GetEntityByNameMixin): :return: :class:`BoundAction ` """ response = self._client.request( - url="/primary_ips/{primary_ip_id}/actions/assign".format( - primary_ip_id=primary_ip.id - ), + url=f"/primary_ips/{primary_ip.id}/actions/assign", method="POST", json={"assignee_id": assignee_id, "assignee_type": assignee_type}, ) return BoundAction(self._client.actions, response["action"]) - def unassign(self, primary_ip): - # type: (PrimaryIP) -> BoundAction + def unassign(self, primary_ip: PrimaryIP | BoundPrimaryIP) -> BoundAction: """Unassigns a Primary IP, resulting in it being unreachable. You may assign it to a server again at a later time. :param primary_ip: :class:`BoundPrimaryIP ` or :class:`PrimaryIP ` :return: :class:`BoundAction ` """ response = self._client.request( - url="/primary_ips/{primary_ip_id}/actions/unassign".format( - primary_ip_id=primary_ip.id - ), + url=f"/primary_ips/{primary_ip.id}/actions/unassign", method="POST", ) return BoundAction(self._client.actions, response["action"]) - def change_dns_ptr(self, primary_ip, ip, dns_ptr): - # type: (PrimaryIP, str, str) -> BoundAction + def change_dns_ptr( + self, + primary_ip: PrimaryIP | BoundPrimaryIP, + ip: str, + dns_ptr: str, + ) -> BoundAction: """Changes the dns ptr that will appear when getting the dns ptr belonging to this Primary IP. :param primary_ip: :class:`BoundPrimaryIP ` or :class:`PrimaryIP ` @@ -320,9 +343,7 @@ class PrimaryIPsClient(ClientEntityBase, GetEntityByNameMixin): :return: :class:`BoundAction ` """ response = self._client.request( - url="/primary_ips/{primary_ip_id}/actions/change_dns_ptr".format( - primary_ip_id=primary_ip.id - ), + url=f"/primary_ips/{primary_ip.id}/actions/change_dns_ptr", method="POST", json={"ip": ip, "dns_ptr": dns_ptr}, ) diff --git a/plugins/module_utils/vendor/hcloud/primary_ips/domain.py b/plugins/module_utils/vendor/hcloud/primary_ips/domain.py index 2db5122..aeb943f 100644 --- a/plugins/module_utils/vendor/hcloud/primary_ips/domain.py +++ b/plugins/module_utils/vendor/hcloud/primary_ips/domain.py @@ -1,9 +1,18 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + try: from dateutil.parser import isoparse except ImportError: isoparse = None -from ..core.domain import BaseDomain +from ..core import BaseDomain + +if TYPE_CHECKING: + from ..actions import BoundAction + from ..datacenters import BoundDatacenter + from .client import BoundPrimaryIP class PrimaryIP(BaseDomain): @@ -55,19 +64,19 @@ class PrimaryIP(BaseDomain): def __init__( self, - id=None, - type=None, - ip=None, - dns_ptr=None, - datacenter=None, - blocked=None, - protection=None, - labels=None, - created=None, - name=None, - assignee_id=None, - assignee_type=None, - auto_delete=None, + id: int | None = None, + type: str | None = None, + ip: str | None = None, + dns_ptr: list[dict] | None = None, + datacenter: BoundDatacenter | None = None, + blocked: bool | None = None, + protection: dict | None = None, + labels: dict[str, dict] | None = None, + created: str | None = None, + name: str | None = None, + assignee_id: int | None = None, + assignee_type: str | None = None, + auto_delete: bool | None = None, ): self.id = id self.type = type @@ -97,8 +106,8 @@ class CreatePrimaryIPResponse(BaseDomain): def __init__( self, - primary_ip, # type: BoundPrimaryIP - action, # type: BoundAction + primary_ip: BoundPrimaryIP, + action: BoundAction | None, ): self.primary_ip = primary_ip self.action = action diff --git a/plugins/module_utils/vendor/hcloud/py.typed b/plugins/module_utils/vendor/hcloud/py.typed new file mode 100644 index 0000000..1242d43 --- /dev/null +++ b/plugins/module_utils/vendor/hcloud/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561. diff --git a/plugins/module_utils/vendor/hcloud/server_types/__init__.py b/plugins/module_utils/vendor/hcloud/server_types/__init__.py index e69de29..1e978d0 100644 --- a/plugins/module_utils/vendor/hcloud/server_types/__init__.py +++ b/plugins/module_utils/vendor/hcloud/server_types/__init__.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .client import ( # noqa: F401 + BoundServerType, + ServerTypesClient, + ServerTypesPageResult, +) +from .domain import ServerType # noqa: F401 diff --git a/plugins/module_utils/vendor/hcloud/server_types/client.py b/plugins/module_utils/vendor/hcloud/server_types/client.py index d572e7b..12cf34a 100644 --- a/plugins/module_utils/vendor/hcloud/server_types/client.py +++ b/plugins/module_utils/vendor/hcloud/server_types/client.py @@ -1,16 +1,29 @@ -from ..core.client import BoundModelBase, ClientEntityBase, GetEntityByNameMixin +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..core import BoundModelBase, ClientEntityBase, Meta from .domain import ServerType +if TYPE_CHECKING: + from .._client import Client + class BoundServerType(BoundModelBase): + _client: ServerTypesClient + model = ServerType -class ServerTypesClient(ClientEntityBase, GetEntityByNameMixin): - results_list_attribute_name = "server_types" +class ServerTypesPageResult(NamedTuple): + server_types: list[BoundServerType] + meta: Meta | None - def get_by_id(self, id): - # type: (int) -> BoundServerType + +class ServerTypesClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundServerType: """Returns a specific Server Type. :param id: int @@ -19,8 +32,12 @@ class ServerTypesClient(ClientEntityBase, GetEntityByNameMixin): response = self._client.request(url=f"/server_types/{id}", method="GET") return BoundServerType(self, response["server_type"]) - def get_list(self, name=None, page=None, per_page=None): - # type: (Optional[str], Optional[int], Optional[int]) -> PageResults[List[BoundServerType], Meta] + def get_list( + self, + name: str | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ServerTypesPageResult: """Get a list of Server types :param name: str (optional) @@ -31,7 +48,7 @@ class ServerTypesClient(ClientEntityBase, GetEntityByNameMixin): Specifies how many results are returned by page :return: (List[:class:`BoundServerType `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if name is not None: params["name"] = name if page is not None: @@ -46,24 +63,22 @@ class ServerTypesClient(ClientEntityBase, GetEntityByNameMixin): BoundServerType(self, server_type_data) for server_type_data in response["server_types"] ] - return self._add_meta_to_result(server_types, response) + return ServerTypesPageResult(server_types, Meta.parse_meta(response)) - def get_all(self, name=None): - # type: (Optional[str]) -> List[BoundServerType] + def get_all(self, name: str | None = None) -> list[BoundServerType]: """Get all Server types :param name: str (optional) Can be used to filter server type by their name. :return: List[:class:`BoundServerType `] """ - return super().get_all(name=name) + return self._iter_pages(self.get_list, name=name) - def get_by_name(self, name): - # type: (str) -> BoundServerType + def get_by_name(self, name: str) -> BoundServerType | None: """Get Server type by name :param name: str Used to get Server type by name. :return: :class:`BoundServerType ` """ - return super().get_by_name(name) + return self._get_first_by(name=name) diff --git a/plugins/module_utils/vendor/hcloud/server_types/domain.py b/plugins/module_utils/vendor/hcloud/server_types/domain.py index bd2e26c..ab2553b 100644 --- a/plugins/module_utils/vendor/hcloud/server_types/domain.py +++ b/plugins/module_utils/vendor/hcloud/server_types/domain.py @@ -1,5 +1,7 @@ -from ..core.domain import BaseDomain, DomainIdentityMixin -from ..deprecation.domain import DeprecationInfo +from __future__ import annotations + +from ..core import BaseDomain, DomainIdentityMixin +from ..deprecation import DeprecationInfo class ServerType(BaseDomain, DomainIdentityMixin): @@ -52,19 +54,19 @@ class ServerType(BaseDomain, DomainIdentityMixin): def __init__( self, - id=None, - name=None, - description=None, - cores=None, - memory=None, - disk=None, - prices=None, - storage_type=None, - cpu_type=None, - architecture=None, - deprecated=None, - deprecation=None, - included_traffic=None, + id: int | None = None, + name: str | None = None, + description: str | None = None, + cores: int | None = None, + memory: int | None = None, + disk: int | None = None, + prices: dict | None = None, + storage_type: str | None = None, + cpu_type: str | None = None, + architecture: str | None = None, + deprecated: bool | None = None, + deprecation: dict | None = None, + included_traffic: int | None = None, ): self.id = id self.name = name diff --git a/plugins/module_utils/vendor/hcloud/servers/__init__.py b/plugins/module_utils/vendor/hcloud/servers/__init__.py index e69de29..a7a61d4 100644 --- a/plugins/module_utils/vendor/hcloud/servers/__init__.py +++ b/plugins/module_utils/vendor/hcloud/servers/__init__.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +from .client import BoundServer, ServersClient, ServersPageResult # noqa: F401 +from .domain import ( # noqa: F401 + CreateServerResponse, + EnableRescueResponse, + IPv4Address, + IPv6Network, + PrivateNet, + PublicNetwork, + PublicNetworkFirewall, + RequestConsoleResponse, + ResetPasswordResponse, + Server, + ServerCreatePublicNetwork, +) diff --git a/plugins/module_utils/vendor/hcloud/servers/client.py b/plugins/module_utils/vendor/hcloud/servers/client.py index b88c69b..c128288 100644 --- a/plugins/module_utils/vendor/hcloud/servers/client.py +++ b/plugins/module_utils/vendor/hcloud/servers/client.py @@ -1,18 +1,20 @@ -from ..actions.client import BoundAction -from ..core.client import BoundModelBase, ClientEntityBase, GetEntityByNameMixin -from ..core.domain import add_meta_to_result -from ..datacenters.client import BoundDatacenter -from ..firewalls.client import BoundFirewall -from ..floating_ips.client import BoundFloatingIP -from ..images.client import BoundImage -from ..images.domain import CreateImageResponse -from ..isos.client import BoundIso -from ..networks.client import BoundNetwork # noqa -from ..networks.domain import Network # noqa -from ..placement_groups.client import BoundPlacementGroup -from ..primary_ips.client import BoundPrimaryIP -from ..server_types.client import BoundServerType -from ..volumes.client import BoundVolume +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..actions import ActionsPageResult, BoundAction +from ..core import BoundModelBase, ClientEntityBase, Meta +from ..datacenters import BoundDatacenter +from ..firewalls import BoundFirewall +from ..floating_ips import BoundFloatingIP +from ..images import BoundImage, CreateImageResponse +from ..isos import BoundIso +from ..networks import BoundNetwork # noqa +from ..networks import Network # noqa +from ..placement_groups import BoundPlacementGroup +from ..primary_ips import BoundPrimaryIP +from ..server_types import BoundServerType +from ..volumes import BoundVolume from .domain import ( CreateServerResponse, EnableRescueResponse, @@ -26,11 +28,26 @@ from .domain import ( Server, ) +if TYPE_CHECKING: + from .._client import Client + from ..datacenters import Datacenter + from ..firewalls import Firewall + from ..images import Image + from ..isos import Iso + from ..locations import BoundLocation, Location + from ..placement_groups import PlacementGroup + from ..server_types import ServerType + from ..ssh_keys import BoundSSHKey, SSHKey + from ..volumes import Volume + from .domain import ServerCreatePublicNetwork + class BoundServer(BoundModelBase): + _client: ServersClient + model = Server - def __init__(self, client, data, complete=True): + def __init__(self, client: ServersClient, data: dict, complete: bool = True): datacenter = data.get("datacenter") if datacenter is not None: data["datacenter"] = BoundDatacenter(client._client.datacenters, datacenter) @@ -137,8 +154,13 @@ class BoundServer(BoundModelBase): super().__init__(client, data, complete) - def get_actions_list(self, status=None, sort=None, page=None, per_page=None): - # type: (Optional[List[str]], Optional[List[str]], Optional[int], Optional[int]) -> PageResults[List[BoundAction, Meta]] + def get_actions_list( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: """Returns all action objects for a server. :param status: List[str] (optional) @@ -153,8 +175,11 @@ class BoundServer(BoundModelBase): """ return self._client.get_actions_list(self, status, sort, page, per_page) - def get_actions(self, status=None, sort=None): - # type: (Optional[List[str]], Optional[List[str]]) -> List[BoundAction] + def get_actions( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: """Returns all action objects for a server. :param status: List[str] (optional) @@ -165,8 +190,11 @@ class BoundServer(BoundModelBase): """ return self._client.get_actions(self, status, sort) - def update(self, name=None, labels=None): - # type: (Optional[str], Optional[Dict[str, str]]) -> BoundServer + def update( + self, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundServer: """Updates a server. You can update a server’s name and a server’s labels. :param name: str (optional) @@ -177,64 +205,60 @@ class BoundServer(BoundModelBase): """ return self._client.update(self, name, labels) - def delete(self): - # type: () -> BoundAction + def delete(self) -> BoundAction: """Deletes a server. This immediately removes the server from your account, and it is no longer accessible. :return: :class:`BoundAction ` """ return self._client.delete(self) - def power_off(self): - # type: () -> BoundAction + def power_off(self) -> BoundAction: """Cuts power to the server. This forcefully stops it without giving the server operating system time to gracefully stop :return: :class:`BoundAction ` """ return self._client.power_off(self) - def power_on(self): - # type: () -> BoundAction + def power_on(self) -> BoundAction: """Starts a server by turning its power on. :return: :class:`BoundAction ` """ return self._client.power_on(self) - def reboot(self): - # type: () -> BoundAction + def reboot(self) -> BoundAction: """Reboots a server gracefully by sending an ACPI request. :return: :class:`BoundAction ` """ return self._client.reboot(self) - def reset(self): - # type: () -> BoundAction + def reset(self) -> BoundAction: """Cuts power to a server and starts it again. :return: :class:`BoundAction ` """ return self._client.reset(self) - def shutdown(self): - # type: () -> BoundAction + def shutdown(self) -> BoundAction: """Shuts down a server gracefully by sending an ACPI shutdown request. :return: :class:`BoundAction ` """ return self._client.shutdown(self) - def reset_password(self): - # type: () -> ResetPasswordResponse + def reset_password(self) -> ResetPasswordResponse: """Resets the root password. Only works for Linux systems that are running the qemu guest agent. :return: :class:`ResetPasswordResponse ` """ return self._client.reset_password(self) - def enable_rescue(self, type=None, ssh_keys=None): - # type: (str, Optional[List[str]]) -> EnableRescueResponse + def enable_rescue( + self, + type: str | None = None, + ssh_keys: list[str] | None = None, + ) -> EnableRescueResponse: """Enable the Hetzner Rescue System for this server. :param type: str @@ -246,16 +270,19 @@ class BoundServer(BoundModelBase): """ return self._client.enable_rescue(self, type=type, ssh_keys=ssh_keys) - def disable_rescue(self): - # type: () -> BoundAction + def disable_rescue(self) -> BoundAction: """Disables the Hetzner Rescue System for a server. :return: :class:`BoundAction ` """ return self._client.disable_rescue(self) - def create_image(self, description=None, type=None, labels=None): - # type: (str, str, Optional[Dict[str, str]]) -> CreateImageResponse + def create_image( + self, + description: str | None = None, + type: str | None = None, + labels: dict[str, str] | None = None, + ) -> CreateImageResponse: """Creates an image (snapshot) from a server by copying the contents of its disks. :param description: str (optional) @@ -269,8 +296,7 @@ class BoundServer(BoundModelBase): """ return self._client.create_image(self, description, type, labels) - def rebuild(self, image): - # type: (Image) -> BoundAction + def rebuild(self, image: Image | BoundImage) -> BoundAction: """Rebuilds a server overwriting its disk with the content of an image, thereby destroying all data on the target server. :param image: :class:`BoundImage ` or :class:`Image ` @@ -278,8 +304,11 @@ class BoundServer(BoundModelBase): """ return self._client.rebuild(self, image) - def change_type(self, server_type, upgrade_disk): - # type: (BoundServerType, bool) -> BoundAction + def change_type( + self, + server_type: ServerType | BoundServerType, + upgrade_disk: bool, + ) -> BoundAction: """Changes the type (Cores, RAM and disk sizes) of a server. :param server_type: :class:`BoundServerType ` or :class:`ServerType ` @@ -290,24 +319,21 @@ class BoundServer(BoundModelBase): """ return self._client.change_type(self, server_type, upgrade_disk) - def enable_backup(self): - # type: () -> BoundAction + def enable_backup(self) -> BoundAction: """Enables and configures the automatic daily backup option for the server. Enabling automatic backups will increase the price of the server by 20%. :return: :class:`BoundAction ` """ return self._client.enable_backup(self) - def disable_backup(self): - # type: () -> BoundAction + def disable_backup(self) -> BoundAction: """Disables the automatic backup option and deletes all existing Backups for a Server. :return: :class:`BoundAction ` """ return self._client.disable_backup(self) - def attach_iso(self, iso): - # type: (Iso) -> BoundAction + def attach_iso(self, iso: Iso | BoundIso) -> BoundAction: """Attaches an ISO to a server. :param iso: :class:`BoundIso ` or :class:`Server ` @@ -315,16 +341,14 @@ class BoundServer(BoundModelBase): """ return self._client.attach_iso(self, iso) - def detach_iso(self): - # type: () -> BoundAction + def detach_iso(self) -> BoundAction: """Detaches an ISO from a server. :return: :class:`BoundAction ` """ return self._client.detach_iso(self) - def change_dns_ptr(self, ip, dns_ptr): - # type: (str, Optional[str]) -> BoundAction + def change_dns_ptr(self, ip: str, dns_ptr: str | None) -> BoundAction: """Changes the hostname that will appear when getting the hostname belonging to the primary IPs (ipv4 and ipv6) of this server. :param ip: str @@ -335,8 +359,11 @@ class BoundServer(BoundModelBase): """ return self._client.change_dns_ptr(self, ip, dns_ptr) - def change_protection(self, delete=None, rebuild=None): - # type: (Optional[bool], Optional[bool]) -> BoundAction + def change_protection( + self, + delete: bool | None = None, + rebuild: bool | None = None, + ) -> BoundAction: """Changes the protection configuration of the server. :param server: :class:`BoundServer ` or :class:`Server ` @@ -348,16 +375,19 @@ class BoundServer(BoundModelBase): """ return self._client.change_protection(self, delete, rebuild) - def request_console(self): - # type: () -> RequestConsoleResponse + def request_console(self) -> RequestConsoleResponse: """Requests credentials for remote access via vnc over websocket to keyboard, monitor, and mouse for a server. :return: :class:`RequestConsoleResponse ` """ return self._client.request_console(self) - def attach_to_network(self, network, ip=None, alias_ips=None): - # type: (Union[Network,BoundNetwork],Optional[str], Optional[List[str]]) -> BoundAction + def attach_to_network( + self, + network: Network | BoundNetwork, + ip: str | None = None, + alias_ips: list[str] | None = None, + ) -> BoundAction: """Attaches a server to a network :param network: :class:`BoundNetwork ` or :class:`Network ` @@ -369,8 +399,7 @@ class BoundServer(BoundModelBase): """ return self._client.attach_to_network(self, network, ip, alias_ips) - def detach_from_network(self, network): - # type: ( Union[Network,BoundNetwork]) -> BoundAction + def detach_from_network(self, network: Network | BoundNetwork) -> BoundAction: """Detaches a server from a network. :param network: :class:`BoundNetwork ` or :class:`Network ` @@ -378,8 +407,11 @@ class BoundServer(BoundModelBase): """ return self._client.detach_from_network(self, network) - def change_alias_ips(self, network, alias_ips): - # type: (Union[Network,BoundNetwork], List[str]) -> BoundAction + def change_alias_ips( + self, + network: Network | BoundNetwork, + alias_ips: list[str], + ) -> BoundAction: """Changes the alias IPs of an already attached network. :param network: :class:`BoundNetwork ` or :class:`Network ` @@ -389,8 +421,10 @@ class BoundServer(BoundModelBase): """ return self._client.change_alias_ips(self, network, alias_ips) - def add_to_placement_group(self, placement_group): - # type: (Union[PlacementGroup,BoundPlacementGroup]) -> BoundAction + def add_to_placement_group( + self, + placement_group: PlacementGroup | BoundPlacementGroup, + ) -> BoundAction: """Adds a server to a placement group. :param placement_group: :class:`BoundPlacementGroup ` or :class:`Network ` @@ -398,8 +432,7 @@ class BoundServer(BoundModelBase): """ return self._client.add_to_placement_group(self, placement_group) - def remove_from_placement_group(self): - # type: () -> BoundAction + def remove_from_placement_group(self) -> BoundAction: """Removes a server from a placement group. :return: :class:`BoundAction ` @@ -407,11 +440,15 @@ class BoundServer(BoundModelBase): return self._client.remove_from_placement_group(self) -class ServersClient(ClientEntityBase, GetEntityByNameMixin): - results_list_attribute_name = "servers" +class ServersPageResult(NamedTuple): + servers: list[BoundServer] + meta: Meta | None - def get_by_id(self, id): - # type: (int) -> BoundServer + +class ServersClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundServer: """Get a specific server :param id: int @@ -422,13 +459,12 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): def get_list( self, - name=None, # type: Optional[str] - label_selector=None, # type: Optional[str] - page=None, # type: Optional[int] - per_page=None, # type: Optional[int] - status=None, # type: Optional[List[str]] - ): - # type: (...) -> PageResults[List[BoundServer], Meta] + name: str | None = None, + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + status: list[str] | None = None, + ) -> ServersPageResult: """Get a list of servers from this account :param name: str (optional) @@ -443,7 +479,7 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): Specifies how many results are returned by page :return: (List[:class:`BoundServer `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if name is not None: params["name"] = name if label_selector is not None: @@ -460,10 +496,14 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): ass_servers = [ BoundServer(self, server_data) for server_data in response["servers"] ] - return self._add_meta_to_result(ass_servers, response) + return ServersPageResult(ass_servers, Meta.parse_meta(response)) - def get_all(self, name=None, label_selector=None, status=None): - # type: (Optional[str], Optional[str], Optional[List[str]]) -> List[BoundServer] + def get_all( + self, + name: str | None = None, + label_selector: str | None = None, + status: list[str] | None = None, + ) -> list[BoundServer]: """Get all servers from this account :param name: str (optional) @@ -474,37 +514,40 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): Can be used to filter servers by their status. The response will only contain servers matching the status. :return: List[:class:`BoundServer `] """ - return super().get_all(name=name, label_selector=label_selector, status=status) + return self._iter_pages( + self.get_list, + name=name, + label_selector=label_selector, + status=status, + ) - def get_by_name(self, name): - # type: (str) -> BoundServer + def get_by_name(self, name: str) -> BoundServer | None: """Get server by name :param name: str Used to get server by name. :return: :class:`BoundServer ` """ - return super().get_by_name(name) + return self._get_first_by(name=name) def create( self, - name, # type: str - server_type, # type: ServerType - image, # type: Image - ssh_keys=None, # type: Optional[List[SSHKey]] - volumes=None, # type: Optional[List[Volume]] - firewalls=None, # type: Optional[List[Firewall]] - networks=None, # type: Optional[List[Network]] - user_data=None, # type: Optional[str] - labels=None, # type: Optional[Dict[str, str]] - location=None, # type: Optional[Location] - datacenter=None, # type: Optional[Datacenter] - start_after_create=True, # type: Optional[bool] - automount=None, # type: Optional[bool] - placement_group=None, # type: Optional[PlacementGroup] - public_net=None, # type: Optional[ServerCreatePublicNetwork] - ): - # type: (...) -> CreateServerResponse + name: str, + server_type: ServerType | BoundServerType, + image: Image, + ssh_keys: list[SSHKey | BoundSSHKey] | None = None, + volumes: list[Volume | BoundVolume] | None = None, + firewalls: list[Firewall | BoundFirewall] | None = None, + networks: list[Network | BoundNetwork] | None = None, + user_data: str | None = None, + labels: dict[str, str] | None = None, + location: Location | BoundLocation | None = None, + datacenter: Datacenter | BoundDatacenter | None = None, + start_after_create: bool | None = True, + automount: bool | None = None, + placement_group: PlacementGroup | BoundPlacementGroup | None = None, + public_net: ServerCreatePublicNetwork | None = None, + ) -> CreateServerResponse: """Creates a new server. Returns preliminary information about the server as well as an action that covers progress of creation. :param name: str @@ -535,7 +578,7 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): Options to configure the public network of a server on creation :return: :class:`CreateServerResponse ` """ - data = { + data: dict[str, Any] = { "name": name, "server_type": server_type.id_or_name, "start_after_create": start_after_create, @@ -564,15 +607,15 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): data["placement_group"] = placement_group.id if public_net is not None: - pn = { + data_public_net: dict[str, Any] = { "enable_ipv4": public_net.enable_ipv4, "enable_ipv6": public_net.enable_ipv6, } if public_net.ipv4 is not None: - pn.update({"ipv4": public_net.ipv4.id}) + data_public_net["ipv4"] = public_net.ipv4.id if public_net.ipv6 is not None: - pn.update({"ipv6": public_net.ipv6.id}) - data["public_net"] = pn + data_public_net["ipv6"] = public_net.ipv6.id + data["public_net"] = data_public_net response = self._client.request(url="/servers", method="POST", json=data) @@ -588,9 +631,13 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): return result def get_actions_list( - self, server, status=None, sort=None, page=None, per_page=None - ): - # type: (Server, Optional[List[str]], Optional[List[str]], Optional[int], Optional[int]) -> PageResults[List[BoundAction], Meta] + self, + server: Server | BoundServer, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: """Returns all action objects for a server. :param server: :class:`BoundServer ` or :class:`Server ` @@ -604,7 +651,7 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): Specifies how many results are returned by page :return: (List[:class:`BoundAction `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if status is not None: params["status"] = status if sort is not None: @@ -623,10 +670,14 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): BoundAction(self._client.actions, action_data) for action_data in response["actions"] ] - return add_meta_to_result(actions, response, "actions") + return ActionsPageResult(actions, Meta.parse_meta(response)) - def get_actions(self, server, status=None, sort=None): - # type: (Server, Optional[List[str]], Optional[List[str]]) -> List[BoundAction] + def get_actions( + self, + server: Server | BoundServer, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: """Returns all action objects for a server. :param server: :class:`BoundServer ` or :class:`Server ` @@ -636,10 +687,19 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` :return: List[:class:`BoundAction `] """ - return super().get_actions(server, status=status, sort=sort) + return self._iter_pages( + self.get_actions_list, + server, + status=status, + sort=sort, + ) - def update(self, server, name=None, labels=None): - # type:(Server, Optional[str], Optional[Dict[str, str]]) -> BoundServer + def update( + self, + server: Server | BoundServer, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundServer: """Updates a server. You can update a server’s name and a server’s labels. :param server: :class:`BoundServer ` or :class:`Server ` @@ -649,7 +709,7 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): User-defined labels (key-value pairs) :return: :class:`BoundServer ` """ - data = {} + data: dict[str, Any] = {} if name is not None: data.update({"name": name}) if labels is not None: @@ -661,8 +721,7 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundServer(self, response["server"]) - def delete(self, server): - # type: (Server) -> BoundAction + def delete(self, server: Server | BoundServer) -> BoundAction: """Deletes a server. This immediately removes the server from your account, and it is no longer accessible. :param server: :class:`BoundServer ` or :class:`Server ` @@ -671,8 +730,7 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): response = self._client.request(url=f"/servers/{server.id}", method="DELETE") return BoundAction(self._client.actions, response["action"]) - def power_off(self, server): - # type: (Server) -> Action + def power_off(self, server: Server | BoundServer) -> BoundAction: """Cuts power to the server. This forcefully stops it without giving the server operating system time to gracefully stop :param server: :class:`BoundServer ` or :class:`Server ` @@ -684,8 +742,7 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundAction(self._client.actions, response["action"]) - def power_on(self, server): - # type: (servers.domain.Server) -> actions.domain.Action + def power_on(self, server: Server | BoundServer) -> BoundAction: """Starts a server by turning its power on. :param server: :class:`BoundServer ` or :class:`Server ` @@ -697,8 +754,7 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundAction(self._client.actions, response["action"]) - def reboot(self, server): - # type: (servers.domain.Server) -> actions.domain.Action + def reboot(self, server: Server | BoundServer) -> BoundAction: """Reboots a server gracefully by sending an ACPI request. :param server: :class:`BoundServer ` or :class:`Server ` @@ -710,8 +766,7 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundAction(self._client.actions, response["action"]) - def reset(self, server): - # type: (servers.domain.Server) -> actions.domainAction + def reset(self, server: Server | BoundServer) -> BoundAction: """Cuts power to a server and starts it again. :param server: :class:`BoundServer ` or :class:`Server ` @@ -723,8 +778,7 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundAction(self._client.actions, response["action"]) - def shutdown(self, server): - # type: (servers.domain.Server) -> actions.domainAction + def shutdown(self, server: Server | BoundServer) -> BoundAction: """Shuts down a server gracefully by sending an ACPI shutdown request. :param server: :class:`BoundServer ` or :class:`Server ` @@ -736,17 +790,14 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundAction(self._client.actions, response["action"]) - def reset_password(self, server): - # type: (servers.domain.Server) -> ResetPasswordResponse + def reset_password(self, server: Server | BoundServer) -> ResetPasswordResponse: """Resets the root password. Only works for Linux systems that are running the qemu guest agent. :param server: :class:`BoundServer ` or :class:`Server ` :return: :class:`ResetPasswordResponse ` """ response = self._client.request( - url="/servers/{server_id}/actions/reset_password".format( - server_id=server.id - ), + url=f"/servers/{server.id}/actions/reset_password", method="POST", ) return ResetPasswordResponse( @@ -754,8 +805,12 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): root_password=response["root_password"], ) - def change_type(self, server, server_type, upgrade_disk): - # type: (servers.domain.Server, BoundServerType, bool) -> actions.domainAction + def change_type( + self, + server: Server | BoundServer, + server_type: ServerType | BoundServerType, + upgrade_disk: bool, + ) -> BoundAction: """Changes the type (Cores, RAM and disk sizes) of a server. :param server: :class:`BoundServer ` or :class:`Server ` @@ -765,7 +820,10 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): If false, do not upgrade the disk. This allows downgrading the server type later. :return: :class:`BoundAction ` """ - data = {"server_type": server_type.id_or_name, "upgrade_disk": upgrade_disk} + data: dict[str, Any] = { + "server_type": server_type.id_or_name, + "upgrade_disk": upgrade_disk, + } response = self._client.request( url=f"/servers/{server.id}/actions/change_type", method="POST", @@ -773,8 +831,12 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundAction(self._client.actions, response["action"]) - def enable_rescue(self, server, type=None, ssh_keys=None): - # type: (servers.domain.Server, str, Optional[List[str]]) -> EnableRescueResponse + def enable_rescue( + self, + server: Server | BoundServer, + type: str | None = None, + ssh_keys: list[str] | None = None, + ) -> EnableRescueResponse: """Enable the Hetzner Rescue System for this server. :param server: :class:`BoundServer ` or :class:`Server ` @@ -785,14 +847,12 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): Array of SSH key IDs which should be injected into the rescue system. Only available for types: linux64 and linux32. :return: :class:`EnableRescueResponse ` """ - data = {"type": type} + data: dict[str, Any] = {"type": type} if ssh_keys is not None: data.update({"ssh_keys": ssh_keys}) response = self._client.request( - url="/servers/{server_id}/actions/enable_rescue".format( - server_id=server.id - ), + url=f"/servers/{server.id}/actions/enable_rescue", method="POST", json=data, ) @@ -801,23 +861,25 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): root_password=response["root_password"], ) - def disable_rescue(self, server): - # type: (servers.domain.Server) -> actions.domainAction + def disable_rescue(self, server: Server | BoundServer) -> BoundAction: """Disables the Hetzner Rescue System for a server. :param server: :class:`BoundServer ` or :class:`Server ` :return: :class:`BoundAction ` """ response = self._client.request( - url="/servers/{server_id}/actions/disable_rescue".format( - server_id=server.id - ), + url=f"/servers/{server.id}/actions/disable_rescue", method="POST", ) return BoundAction(self._client.actions, response["action"]) - def create_image(self, server, description=None, type=None, labels=None): - # type: (servers.domain.Server, str, str, Optional[Dict[str, str]]) -> CreateImageResponse + def create_image( + self, + server: Server | BoundServer, + description: str | None = None, + type: str | None = None, + labels: dict[str, str] | None = None, + ) -> CreateImageResponse: """Creates an image (snapshot) from a server by copying the contents of its disks. :param server: :class:`BoundServer ` or :class:`Server ` @@ -830,7 +892,7 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): User-defined labels (key-value pairs) :return: :class:`CreateImageResponse ` """ - data = {} + data: dict[str, Any] = {} if description is not None: data.update({"description": description}) @@ -850,15 +912,18 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): image=BoundImage(self._client.images, response["image"]), ) - def rebuild(self, server, image): - # type: (servers.domain.Server, Image) -> actions.domainAction + def rebuild( + self, + server: Server | BoundServer, + image: Image | BoundImage, + ) -> BoundAction: """Rebuilds a server overwriting its disk with the content of an image, thereby destroying all data on the target server. :param server: :class:`BoundServer ` or :class:`Server ` :param image: :class:`BoundImage ` or :class:`Image ` :return: :class:`BoundAction ` """ - data = {"image": image.id_or_name} + data: dict[str, Any] = {"image": image.id_or_name} response = self._client.request( url=f"/servers/{server.id}/actions/rebuild", method="POST", @@ -866,45 +931,42 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundAction(self._client.actions, response["action"]) - def enable_backup(self, server): - # type: (servers.domain.Server) -> actions.domainAction + def enable_backup(self, server: Server | BoundServer) -> BoundAction: """Enables and configures the automatic daily backup option for the server. Enabling automatic backups will increase the price of the server by 20%. :param server: :class:`BoundServer ` or :class:`Server ` :return: :class:`BoundAction ` """ response = self._client.request( - url="/servers/{server_id}/actions/enable_backup".format( - server_id=server.id - ), + url=f"/servers/{server.id}/actions/enable_backup", method="POST", ) return BoundAction(self._client.actions, response["action"]) - def disable_backup(self, server): - # type: (servers.domain.Server) -> actions.domainAction + def disable_backup(self, server: Server | BoundServer) -> BoundAction: """Disables the automatic backup option and deletes all existing Backups for a Server. :param server: :class:`BoundServer ` or :class:`Server ` :return: :class:`BoundAction ` """ response = self._client.request( - url="/servers/{server_id}/actions/disable_backup".format( - server_id=server.id - ), + url=f"/servers/{server.id}/actions/disable_backup", method="POST", ) return BoundAction(self._client.actions, response["action"]) - def attach_iso(self, server, iso): - # type: (servers.domain.Server, Iso) -> actions.domainAction + def attach_iso( + self, + server: Server | BoundServer, + iso: Iso | BoundIso, + ) -> BoundAction: """Attaches an ISO to a server. :param server: :class:`BoundServer ` or :class:`Server ` :param iso: :class:`BoundIso ` or :class:`Server ` :return: :class:`BoundAction ` """ - data = {"iso": iso.id_or_name} + data: dict[str, Any] = {"iso": iso.id_or_name} response = self._client.request( url=f"/servers/{server.id}/actions/attach_iso", method="POST", @@ -912,8 +974,7 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundAction(self._client.actions, response["action"]) - def detach_iso(self, server): - # type: (servers.domain.Server) -> actions.domainAction + def detach_iso(self, server: Server | BoundServer) -> BoundAction: """Detaches an ISO from a server. :param server: :class:`BoundServer ` or :class:`Server ` @@ -925,8 +986,12 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundAction(self._client.actions, response["action"]) - def change_dns_ptr(self, server, ip, dns_ptr): - # type: (servers.domain.Server, str, str) -> actions.domainAction + def change_dns_ptr( + self, + server: Server | BoundServer, + ip: str, + dns_ptr: str | None, + ) -> BoundAction: """Changes the hostname that will appear when getting the hostname belonging to the primary IPs (ipv4 and ipv6) of this server. :param server: :class:`BoundServer ` or :class:`Server ` @@ -936,18 +1001,20 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): Hostname to set as a reverse DNS PTR entry, will reset to original default value if `None` :return: :class:`BoundAction ` """ - data = {"ip": ip, "dns_ptr": dns_ptr} + data: dict[str, Any] = {"ip": ip, "dns_ptr": dns_ptr} response = self._client.request( - url="/servers/{server_id}/actions/change_dns_ptr".format( - server_id=server.id - ), + url=f"/servers/{server.id}/actions/change_dns_ptr", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def change_protection(self, server, delete=None, rebuild=None): - # type: (servers.domain.Server, Optional[bool], Optional[bool]) -> actions.domainAction + def change_protection( + self, + server: Server | BoundServer, + delete: bool | None = None, + rebuild: bool | None = None, + ) -> BoundAction: """Changes the protection configuration of the server. :param server: :class:`BoundServer ` or :class:`Server ` @@ -957,32 +1024,27 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): If true, prevents the server from being rebuilt (currently delete and rebuild attribute needs to have the same value) :return: :class:`BoundAction ` """ - data = {} + data: dict[str, Any] = {} if delete is not None: data.update({"delete": delete}) if rebuild is not None: data.update({"rebuild": rebuild}) response = self._client.request( - url="/servers/{server_id}/actions/change_protection".format( - server_id=server.id - ), + url=f"/servers/{server.id}/actions/change_protection", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def request_console(self, server): - # type: (servers.domain.Server) -> RequestConsoleResponse + def request_console(self, server: Server | BoundServer) -> RequestConsoleResponse: """Requests credentials for remote access via vnc over websocket to keyboard, monitor, and mouse for a server. :param server: :class:`BoundServer ` or :class:`Server ` :return: :class:`RequestConsoleResponse ` """ response = self._client.request( - url="/servers/{server_id}/actions/request_console".format( - server_id=server.id - ), + url=f"/servers/{server.id}/actions/request_console", method="POST", ) return RequestConsoleResponse( @@ -991,8 +1053,13 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): password=response["password"], ) - def attach_to_network(self, server, network, ip=None, alias_ips=None): - # type: (Union[Server,BoundServer], Union[Network,BoundNetwork],Optional[str], Optional[List[str]]) -> BoundAction + def attach_to_network( + self, + server: Server | BoundServer, + network: Network | BoundNetwork, + ip: str | None = None, + alias_ips: list[str] | None = None, + ) -> BoundAction: """Attaches a server to a network :param server: :class:`BoundServer ` or :class:`Server ` @@ -1003,40 +1070,43 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): New alias IPs to set for this server. :return: :class:`BoundAction ` """ - data = {"network": network.id} + data: dict[str, Any] = {"network": network.id} if ip is not None: data.update({"ip": ip}) if alias_ips is not None: data.update({"alias_ips": alias_ips}) response = self._client.request( - url="/servers/{server_id}/actions/attach_to_network".format( - server_id=server.id - ), + url=f"/servers/{server.id}/actions/attach_to_network", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def detach_from_network(self, server, network): - # type: (Union[Server,BoundServer], Union[Network,BoundNetwork]) -> BoundAction + def detach_from_network( + self, + server: Server | BoundServer, + network: Network | BoundNetwork, + ) -> BoundAction: """Detaches a server from a network. :param server: :class:`BoundServer ` or :class:`Server ` :param network: :class:`BoundNetwork ` or :class:`Network ` :return: :class:`BoundAction ` """ - data = {"network": network.id} + data: dict[str, Any] = {"network": network.id} response = self._client.request( - url="/servers/{server_id}/actions/detach_from_network".format( - server_id=server.id - ), + url=f"/servers/{server.id}/actions/detach_from_network", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def change_alias_ips(self, server, network, alias_ips): - # type: (Union[Server,BoundServer], Union[Network,BoundNetwork], List[str]) -> BoundAction + def change_alias_ips( + self, + server: Server | BoundServer, + network: Network | BoundNetwork, + alias_ips: list[str], + ) -> BoundAction: """Changes the alias IPs of an already attached network. :param server: :class:`BoundServer ` or :class:`Server ` @@ -1045,45 +1115,41 @@ class ServersClient(ClientEntityBase, GetEntityByNameMixin): New alias IPs to set for this server. :return: :class:`BoundAction ` """ - data = {"network": network.id, "alias_ips": alias_ips} + data: dict[str, Any] = {"network": network.id, "alias_ips": alias_ips} response = self._client.request( - url="/servers/{server_id}/actions/change_alias_ips".format( - server_id=server.id - ), + url=f"/servers/{server.id}/actions/change_alias_ips", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def add_to_placement_group(self, server, placement_group): - # type: (Union[Server,BoundServer], Union[PlacementGroup,BoundPlacementGroup]) -> BoundAction + def add_to_placement_group( + self, + server: Server | BoundServer, + placement_group: PlacementGroup | BoundPlacementGroup, + ) -> BoundAction: """Adds a server to a placement group. :param server: :class:`BoundServer ` or :class:`Server ` :param placement_group: :class:`BoundPlacementGroup ` or :class:`Network ` :return: :class:`BoundAction ` """ - data = {"placement_group": str(placement_group.id)} + data: dict[str, Any] = {"placement_group": str(placement_group.id)} response = self._client.request( - url="/servers/{server_id}/actions/add_to_placement_group".format( - server_id=server.id - ), + url=f"/servers/{server.id}/actions/add_to_placement_group", method="POST", json=data, ) return BoundAction(self._client.actions, response["action"]) - def remove_from_placement_group(self, server): - # type: (Union[Server,BoundServer]) -> BoundAction + def remove_from_placement_group(self, server: Server | BoundServer) -> BoundAction: """Removes a server from a placement group. :param server: :class:`BoundServer ` or :class:`Server ` :return: :class:`BoundAction ` """ response = self._client.request( - url="/servers/{server_id}/actions/remove_from_placement_group".format( - server_id=server.id - ), + url=f"/servers/{server.id}/actions/remove_from_placement_group", method="POST", ) return BoundAction(self._client.actions, response["action"]) diff --git a/plugins/module_utils/vendor/hcloud/servers/domain.py b/plugins/module_utils/vendor/hcloud/servers/domain.py index e8d537a..09c5526 100644 --- a/plugins/module_utils/vendor/hcloud/servers/domain.py +++ b/plugins/module_utils/vendor/hcloud/servers/domain.py @@ -1,9 +1,27 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + try: from dateutil.parser import isoparse except ImportError: isoparse = None -from ..core.domain import BaseDomain +from ..core import BaseDomain + +if TYPE_CHECKING: + from ..actions import BoundAction + from ..datacenters import BoundDatacenter + from ..firewalls import BoundFirewall + from ..floating_ips import BoundFloatingIP + from ..images import BoundImage + from ..isos import BoundIso + from ..networks import BoundNetwork + from ..placement_groups import BoundPlacementGroup + from ..primary_ips import BoundPrimaryIP, PrimaryIP + from ..server_types import BoundServerType + from ..volumes import BoundVolume + from .client import BoundServer class Server(BaseDomain): @@ -91,27 +109,27 @@ class Server(BaseDomain): def __init__( self, - id, - name=None, - status=None, - created=None, - public_net=None, - server_type=None, - datacenter=None, - image=None, - iso=None, - rescue_enabled=None, - locked=None, - backup_window=None, - outgoing_traffic=None, - ingoing_traffic=None, - included_traffic=None, - protection=None, - labels=None, - volumes=None, - private_net=None, - primary_disk_size=None, - placement_group=None, + id: int, + name: str | None = None, + status: str | None = None, + created: str | None = None, + public_net: PublicNetwork | None = None, + server_type: BoundServerType | None = None, + datacenter: BoundDatacenter | None = None, + image: BoundImage | None = None, + iso: BoundIso | None = None, + rescue_enabled: bool | None = None, + locked: bool | None = None, + backup_window: str | None = None, + outgoing_traffic: int | None = None, + ingoing_traffic: int | None = None, + included_traffic: int | None = None, + protection: dict | None = None, + labels: dict[str, str] | None = None, + volumes: list[BoundVolume] | None = None, + private_net: list[PrivateNet] | None = None, + primary_disk_size: int | None = None, + placement_group: BoundPlacementGroup | None = None, ): self.id = id self.name = name @@ -153,10 +171,10 @@ class CreateServerResponse(BaseDomain): def __init__( self, - server, # type: BoundServer - action, # type: BoundAction - next_actions, # type: List[Action] - root_password, # type: str + server: BoundServer, + action: BoundAction, + next_actions: list[BoundAction], + root_password: str | None, ): self.server = server self.action = action @@ -177,8 +195,8 @@ class ResetPasswordResponse(BaseDomain): def __init__( self, - action, # type: BoundAction - root_password, # type: str + action: BoundAction, + root_password: str, ): self.action = action self.root_password = root_password @@ -197,8 +215,8 @@ class EnableRescueResponse(BaseDomain): def __init__( self, - action, # type: BoundAction - root_password, # type: str + action: BoundAction, + root_password: str, ): self.action = action self.root_password = root_password @@ -219,9 +237,9 @@ class RequestConsoleResponse(BaseDomain): def __init__( self, - action, # type: BoundAction - wss_url, # type: str - password, # type: str + action: BoundAction, + wss_url: str, + password: str, ): self.action = action self.wss_url = wss_url @@ -250,12 +268,12 @@ class PublicNetwork(BaseDomain): def __init__( self, - ipv4, # type: IPv4Address - ipv6, # type: IPv6Network - floating_ips, # type: List[BoundFloatingIP] - primary_ipv4, # type: BoundPrimaryIP - primary_ipv6, # type: BoundPrimaryIP - firewalls=None, # type: List[PublicNetworkFirewall] + ipv4: IPv4Address, + ipv6: IPv6Network, + floating_ips: list[BoundFloatingIP], + primary_ipv4: BoundPrimaryIP | None, + primary_ipv6: BoundPrimaryIP | None, + firewalls: list[PublicNetworkFirewall] | None = None, ): self.ipv4 = ipv4 self.ipv6 = ipv6 @@ -281,8 +299,8 @@ class PublicNetworkFirewall(BaseDomain): def __init__( self, - firewall, # type: BoundFirewall - status, # type: str + firewall: BoundFirewall, + status: str, ): self.firewall = firewall self.status = status @@ -303,9 +321,9 @@ class IPv4Address(BaseDomain): def __init__( self, - ip, # type: str - blocked, # type: bool - dns_ptr, # type: str + ip: str, + blocked: bool, + dns_ptr: str, ): self.ip = ip self.blocked = blocked @@ -331,9 +349,9 @@ class IPv6Network(BaseDomain): def __init__( self, - ip, # type: str - blocked, # type: bool - dns_ptr, # type: list + ip: str, + blocked: bool, + dns_ptr: list, ): self.ip = ip self.blocked = blocked @@ -360,10 +378,10 @@ class PrivateNet(BaseDomain): def __init__( self, - network, # type: BoundNetwork - ip, # type: str - alias_ips, # type: List[str] - mac_address, # type: str + network: BoundNetwork, + ip: str, + alias_ips: list[str], + mac_address: str, ): self.network = network self.ip = ip @@ -384,10 +402,10 @@ class ServerCreatePublicNetwork(BaseDomain): def __init__( self, - ipv4=None, # type: hcloud.primary_ips.domain.PrimaryIP - ipv6=None, # type: hcloud.primary_ips.domain.PrimaryIP - enable_ipv4=True, # type: bool - enable_ipv6=True, # type: bool + ipv4: PrimaryIP | None = None, + ipv6: PrimaryIP | None = None, + enable_ipv4: bool = True, + enable_ipv6: bool = True, ): self.ipv4 = ipv4 self.ipv6 = ipv6 diff --git a/plugins/module_utils/vendor/hcloud/ssh_keys/__init__.py b/plugins/module_utils/vendor/hcloud/ssh_keys/__init__.py index e69de29..2515590 100644 --- a/plugins/module_utils/vendor/hcloud/ssh_keys/__init__.py +++ b/plugins/module_utils/vendor/hcloud/ssh_keys/__init__.py @@ -0,0 +1,4 @@ +from __future__ import annotations + +from .client import BoundSSHKey, SSHKeysClient, SSHKeysPageResult # noqa: F401 +from .domain import SSHKey # noqa: F401 diff --git a/plugins/module_utils/vendor/hcloud/ssh_keys/client.py b/plugins/module_utils/vendor/hcloud/ssh_keys/client.py index e9a9402..8a86da8 100644 --- a/plugins/module_utils/vendor/hcloud/ssh_keys/client.py +++ b/plugins/module_utils/vendor/hcloud/ssh_keys/client.py @@ -1,12 +1,24 @@ -from ..core.client import BoundModelBase, ClientEntityBase, GetEntityByNameMixin +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..core import BoundModelBase, ClientEntityBase, Meta from .domain import SSHKey +if TYPE_CHECKING: + from .._client import Client + class BoundSSHKey(BoundModelBase): + _client: SSHKeysClient + model = SSHKey - def update(self, name=None, labels=None): - # type: (Optional[str], Optional[Dict[str, str]]) -> BoundSSHKey + def update( + self, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundSSHKey: """Updates an SSH key. You can update an SSH key name and an SSH key labels. :param description: str (optional) @@ -17,19 +29,22 @@ class BoundSSHKey(BoundModelBase): """ return self._client.update(self, name, labels) - def delete(self): - # type: () -> bool + def delete(self) -> bool: """Deletes an SSH key. It cannot be used anymore. :return: boolean """ return self._client.delete(self) -class SSHKeysClient(ClientEntityBase, GetEntityByNameMixin): - results_list_attribute_name = "ssh_keys" +class SSHKeysPageResult(NamedTuple): + ssh_keys: list[BoundSSHKey] + meta: Meta | None - def get_by_id(self, id): - # type: (int) -> BoundSSHKey + +class SSHKeysClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundSSHKey: """Get a specific SSH Key by its ID :param id: int @@ -40,13 +55,12 @@ class SSHKeysClient(ClientEntityBase, GetEntityByNameMixin): def get_list( self, - name=None, # type: Optional[str] - fingerprint=None, # type: Optional[str] - label_selector=None, # type: Optional[str] - page=None, # type: Optional[int] - per_page=None, # type: Optional[int] - ): - # type: (...) -> PageResults[List[BoundSSHKey], Meta] + name: str | None = None, + fingerprint: str | None = None, + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> SSHKeysPageResult: """Get a list of SSH keys from the account :param name: str (optional) @@ -61,7 +75,7 @@ class SSHKeysClient(ClientEntityBase, GetEntityByNameMixin): Specifies how many results are returned by page :return: (List[:class:`BoundSSHKey `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if name is not None: params["name"] = name if fingerprint is not None: @@ -75,13 +89,17 @@ class SSHKeysClient(ClientEntityBase, GetEntityByNameMixin): response = self._client.request(url="/ssh_keys", method="GET", params=params) - ass_ssh_keys = [ + ssh_keys = [ BoundSSHKey(self, server_data) for server_data in response["ssh_keys"] ] - return self._add_meta_to_result(ass_ssh_keys, response) + return SSHKeysPageResult(ssh_keys, Meta.parse_meta(response)) - def get_all(self, name=None, fingerprint=None, label_selector=None): - # type: (Optional[str], Optional[str], Optional[str]) -> List[BoundSSHKey] + def get_all( + self, + name: str | None = None, + fingerprint: str | None = None, + label_selector: str | None = None, + ) -> list[BoundSSHKey]: """Get all SSH keys from the account :param name: str (optional) @@ -92,34 +110,37 @@ class SSHKeysClient(ClientEntityBase, GetEntityByNameMixin): Can be used to filter SSH keys by labels. The response will only contain SSH keys matching the label selector. :return: List[:class:`BoundSSHKey `] """ - return super().get_all( - name=name, fingerprint=fingerprint, label_selector=label_selector + return self._iter_pages( + self.get_list, + name=name, + fingerprint=fingerprint, + label_selector=label_selector, ) - def get_by_name(self, name): - # type: (str) -> SSHKeysClient + def get_by_name(self, name: str) -> BoundSSHKey | None: """Get ssh key by name :param name: str Used to get ssh key by name. :return: :class:`BoundSSHKey ` """ - return super().get_by_name(name) + return self._get_first_by(name=name) - def get_by_fingerprint(self, fingerprint): - # type: (str) -> BoundSSHKey + def get_by_fingerprint(self, fingerprint: str) -> BoundSSHKey | None: """Get ssh key by fingerprint :param fingerprint: str Used to get ssh key by fingerprint. :return: :class:`BoundSSHKey ` """ - response = self.get_list(fingerprint=fingerprint) - sshkeys = response.ssh_keys - return sshkeys[0] if sshkeys else None + return self._get_first_by(fingerprint=fingerprint) - def create(self, name, public_key, labels=None): - # type: (str, str, Optional[Dict[str, str]]) -> BoundSSHKey + def create( + self, + name: str, + public_key: str, + labels: dict[str, str] | None = None, + ) -> BoundSSHKey: """Creates a new SSH key with the given name and public_key. :param name: str @@ -129,14 +150,18 @@ class SSHKeysClient(ClientEntityBase, GetEntityByNameMixin): User-defined labels (key-value pairs) :return: :class:`BoundSSHKey ` """ - data = {"name": name, "public_key": public_key} + data: dict[str, Any] = {"name": name, "public_key": public_key} if labels is not None: data["labels"] = labels response = self._client.request(url="/ssh_keys", method="POST", json=data) return BoundSSHKey(self, response["ssh_key"]) - def update(self, ssh_key, name=None, labels=None): - # type: (SSHKey, Optional[str], Optional[Dict[str, str]]) -> BoundSSHKey + def update( + self, + ssh_key: SSHKey | BoundSSHKey, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundSSHKey: """Updates an SSH key. You can update an SSH key name and an SSH key labels. :param ssh_key: :class:`BoundSSHKey ` or :class:`SSHKey ` @@ -146,7 +171,7 @@ class SSHKeysClient(ClientEntityBase, GetEntityByNameMixin): User-defined labels (key-value pairs) :return: :class:`BoundSSHKey ` """ - data = {} + data: dict[str, Any] = {} if name is not None: data["name"] = name if labels is not None: @@ -158,13 +183,12 @@ class SSHKeysClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundSSHKey(self, response["ssh_key"]) - def delete(self, ssh_key): - # type: (SSHKey) -> bool - self._client.request(url=f"/ssh_keys/{ssh_key.id}", method="DELETE") + def delete(self, ssh_key: SSHKey | BoundSSHKey) -> bool: """Deletes an SSH key. It cannot be used anymore. :param ssh_key: :class:`BoundSSHKey ` or :class:`SSHKey ` :return: True """ + self._client.request(url=f"/ssh_keys/{ssh_key.id}", method="DELETE") # Return always true, because the API does not return an action for it. When an error occurs a HcloudAPIException will be raised return True diff --git a/plugins/module_utils/vendor/hcloud/ssh_keys/domain.py b/plugins/module_utils/vendor/hcloud/ssh_keys/domain.py index cf6e198..3c880c4 100644 --- a/plugins/module_utils/vendor/hcloud/ssh_keys/domain.py +++ b/plugins/module_utils/vendor/hcloud/ssh_keys/domain.py @@ -1,9 +1,11 @@ +from __future__ import annotations + try: from dateutil.parser import isoparse except ImportError: isoparse = None -from ..core.domain import BaseDomain, DomainIdentityMixin +from ..core import BaseDomain, DomainIdentityMixin class SSHKey(BaseDomain, DomainIdentityMixin): @@ -27,12 +29,12 @@ class SSHKey(BaseDomain, DomainIdentityMixin): def __init__( self, - id=None, - name=None, - fingerprint=None, - public_key=None, - labels=None, - created=None, + id: int | None = None, + name: str | None = None, + fingerprint: str | None = None, + public_key: str | None = None, + labels: dict[str, str] | None = None, + created: str | None = None, ): self.id = id self.name = name diff --git a/plugins/module_utils/vendor/hcloud/volumes/__init__.py b/plugins/module_utils/vendor/hcloud/volumes/__init__.py index e69de29..dc8ccbb 100644 --- a/plugins/module_utils/vendor/hcloud/volumes/__init__.py +++ b/plugins/module_utils/vendor/hcloud/volumes/__init__.py @@ -0,0 +1,4 @@ +from __future__ import annotations + +from .client import BoundVolume, VolumesClient, VolumesPageResult # noqa: F401 +from .domain import CreateVolumeResponse, Volume # noqa: F401 diff --git a/plugins/module_utils/vendor/hcloud/volumes/client.py b/plugins/module_utils/vendor/hcloud/volumes/client.py index f7f6bef..60b1d9b 100644 --- a/plugins/module_utils/vendor/hcloud/volumes/client.py +++ b/plugins/module_utils/vendor/hcloud/volumes/client.py @@ -1,19 +1,29 @@ -from ..actions.client import BoundAction -from ..core.client import BoundModelBase, ClientEntityBase, GetEntityByNameMixin -from ..core.domain import add_meta_to_result -from ..locations.client import BoundLocation +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..actions import ActionsPageResult, BoundAction +from ..core import BoundModelBase, ClientEntityBase, Meta +from ..locations import BoundLocation from .domain import CreateVolumeResponse, Volume +if TYPE_CHECKING: + from .._client import Client + from ..locations import Location + from ..servers import BoundServer, Server + class BoundVolume(BoundModelBase): + _client: VolumesClient + model = Volume - def __init__(self, client, data, complete=True): + def __init__(self, client: VolumesClient, data: dict, complete: bool = True): location = data.get("location") if location is not None: data["location"] = BoundLocation(client._client.locations, location) - from ..servers.client import BoundServer + from ..servers import BoundServer server = data.get("server") if server is not None: @@ -22,8 +32,13 @@ class BoundVolume(BoundModelBase): ) super().__init__(client, data, complete) - def get_actions_list(self, status=None, sort=None, page=None, per_page=None): - # type: (Optional[List[str]], Optional[List[str]], Optional[int], Optional[int]) -> PageResults[List[BoundAction, Meta]] + def get_actions_list( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: """Returns all action objects for a volume. :param status: List[str] (optional) @@ -38,8 +53,11 @@ class BoundVolume(BoundModelBase): """ return self._client.get_actions_list(self, status, sort, page, per_page) - def get_actions(self, status=None, sort=None): - # type: (Optional[List[str]], Optional[List[str]]) -> List[BoundAction] + def get_actions( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: """Returns all action objects for a volume. :param status: List[str] (optional) @@ -50,8 +68,11 @@ class BoundVolume(BoundModelBase): """ return self._client.get_actions(self, status, sort) - def update(self, name=None, labels=None): - # type: (Optional[str], Optional[Dict[str, str]]) -> BoundAction + def update( + self, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundVolume: """Updates the volume properties. :param name: str (optional) @@ -62,16 +83,18 @@ class BoundVolume(BoundModelBase): """ return self._client.update(self, name, labels) - def delete(self): - # type: () -> BoundAction + def delete(self) -> bool: """Deletes a volume. All volume data is irreversibly destroyed. The volume must not be attached to a server and it must not have delete protection enabled. :return: boolean """ return self._client.delete(self) - def attach(self, server, automount=None): - # type: (Union[Server, BoundServer], Optional[bool]) -> BoundAction + def attach( + self, + server: Server | BoundServer, + automount: bool | None = None, + ) -> BoundAction: """Attaches a volume to a server. Works only if the server is in the same location as the volume. :param server: :class:`BoundServer ` or :class:`Server ` @@ -80,16 +103,14 @@ class BoundVolume(BoundModelBase): """ return self._client.attach(self, server, automount) - def detach(self): - # type: () -> BoundAction + def detach(self) -> BoundAction: """Detaches a volume from the server it’s attached to. You may attach it to a server again at a later time. :return: :class:`BoundAction ` """ return self._client.detach(self) - def resize(self, size): - # type: (int) -> BoundAction + def resize(self, size: int) -> BoundAction: """Changes the size of a volume. Note that downsizing a volume is not possible. :param size: int @@ -98,8 +119,7 @@ class BoundVolume(BoundModelBase): """ return self._client.resize(self, size) - def change_protection(self, delete=None): - # type: (Optional[bool]) -> BoundAction + def change_protection(self, delete: bool | None = None) -> BoundAction: """Changes the protection configuration of a volume. :param delete: boolean @@ -109,11 +129,15 @@ class BoundVolume(BoundModelBase): return self._client.change_protection(self, delete) -class VolumesClient(ClientEntityBase, GetEntityByNameMixin): - results_list_attribute_name = "volumes" +class VolumesPageResult(NamedTuple): + volumes: list[BoundVolume] + meta: Meta | None - def get_by_id(self, id): - # type: (int) -> volumes.client.BoundVolume + +class VolumesClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundVolume: """Get a specific volume by its id :param id: int @@ -123,9 +147,13 @@ class VolumesClient(ClientEntityBase, GetEntityByNameMixin): return BoundVolume(self, response["volume"]) def get_list( - self, name=None, label_selector=None, page=None, per_page=None, status=None - ): - # type: (Optional[str], Optional[str], Optional[int], Optional[int], Optional[List[str]]) -> PageResults[List[BoundVolume], Meta] + self, + name: str | None = None, + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + status: list[str] | None = None, + ) -> VolumesPageResult: """Get a list of volumes from this account :param name: str (optional) @@ -140,7 +168,7 @@ class VolumesClient(ClientEntityBase, GetEntityByNameMixin): Specifies how many results are returned by page :return: (List[:class:`BoundVolume `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if name is not None: params["name"] = name if label_selector is not None: @@ -156,10 +184,13 @@ class VolumesClient(ClientEntityBase, GetEntityByNameMixin): volumes = [ BoundVolume(self, volume_data) for volume_data in response["volumes"] ] - return self._add_meta_to_result(volumes, response) + return VolumesPageResult(volumes, Meta.parse_meta(response)) - def get_all(self, label_selector=None, status=None): - # type: (Optional[str], Optional[List[str]]) -> List[BoundVolume] + def get_all( + self, + label_selector: str | None = None, + status: list[str] | None = None, + ) -> list[BoundVolume]: """Get all volumes from this account :param label_selector: @@ -168,29 +199,31 @@ class VolumesClient(ClientEntityBase, GetEntityByNameMixin): Can be used to filter volumes by their status. The response will only contain volumes matching the status. :return: List[:class:`BoundVolume `] """ - return super().get_all(label_selector=label_selector, status=status) + return self._iter_pages( + self.get_list, + label_selector=label_selector, + status=status, + ) - def get_by_name(self, name): - # type: (str) -> BoundVolume + def get_by_name(self, name: str) -> BoundVolume | None: """Get volume by name :param name: str Used to get volume by name. :return: :class:`BoundVolume ` """ - return super().get_by_name(name) + return self._get_first_by(name=name) def create( self, - size, # type: int - name, # type: str - labels=None, # type: Optional[str] - location=None, # type: Optional[Location] - server=None, # type: Optional[Server], - automount=None, # type: Optional[bool], - format=None, # type: Optional[str], - ): - # type: (...) -> CreateVolumeResponse + size: int, + name: str, + labels: str | None = None, + location: Location | None = None, + server: Server | None = None, + automount: bool | None = None, + format: str | None = None, + ) -> CreateVolumeResponse: """Creates a new volume attached to a server. :param size: int @@ -214,7 +247,7 @@ class VolumesClient(ClientEntityBase, GetEntityByNameMixin): if not (bool(location) ^ bool(server)): raise ValueError("only one of server or location must be provided") - data = {"name": name, "size": size} + data: dict[str, Any] = {"name": name, "size": size} if labels is not None: data["labels"] = labels if location is not None: @@ -240,9 +273,13 @@ class VolumesClient(ClientEntityBase, GetEntityByNameMixin): return result def get_actions_list( - self, volume, status=None, sort=None, page=None, per_page=None - ): - # type: (Volume, Optional[List[str]], Optional[List[str]], Optional[int], Optional[int]) -> PageResults[List[BoundAction], Meta] + self, + volume: Volume | BoundVolume, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: """Returns all action objects for a volume. :param volume: :class:`BoundVolume ` or :class:`Volume ` @@ -256,7 +293,7 @@ class VolumesClient(ClientEntityBase, GetEntityByNameMixin): Specifies how many results are returned by page :return: (List[:class:`BoundAction `], :class:`Meta `) """ - params = {} + params: dict[str, Any] = {} if status is not None: params["status"] = status if sort is not None: @@ -275,10 +312,14 @@ class VolumesClient(ClientEntityBase, GetEntityByNameMixin): BoundAction(self._client.actions, action_data) for action_data in response["actions"] ] - return add_meta_to_result(actions, response, "actions") + return ActionsPageResult(actions, Meta.parse_meta(response)) - def get_actions(self, volume, status=None, sort=None): - # type: (Union[Volume, BoundVolume], Optional[List[str]], Optional[List[str]]) -> List[BoundAction] + def get_actions( + self, + volume: Volume | BoundVolume, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: """Returns all action objects for a volume. :param volume: :class:`BoundVolume ` or :class:`Volume ` @@ -288,10 +329,19 @@ class VolumesClient(ClientEntityBase, GetEntityByNameMixin): Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` :return: List[:class:`BoundAction `] """ - return super().get_actions(volume, status=status, sort=sort) + return self._iter_pages( + self.get_actions_list, + volume, + status=status, + sort=sort, + ) - def update(self, volume, name=None, labels=None): - # type:(Union[Volume, BoundVolume], Optional[str], Optional[Dict[str, str]]) -> BoundVolume + def update( + self, + volume: Volume | BoundVolume, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundVolume: """Updates the volume properties. :param volume: :class:`BoundVolume ` or :class:`Volume ` @@ -301,7 +351,7 @@ class VolumesClient(ClientEntityBase, GetEntityByNameMixin): User-defined labels (key-value pairs) :return: :class:`BoundAction ` """ - data = {} + data: dict[str, Any] = {} if name is not None: data.update({"name": name}) if labels is not None: @@ -313,8 +363,7 @@ class VolumesClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundVolume(self, response["volume"]) - def delete(self, volume): - # type: (Union[Volume, BoundVolume]) -> BoundAction + def delete(self, volume: Volume | BoundVolume) -> bool: """Deletes a volume. All volume data is irreversibly destroyed. The volume must not be attached to a server and it must not have delete protection enabled. :param volume: :class:`BoundVolume ` or :class:`Volume ` @@ -323,8 +372,7 @@ class VolumesClient(ClientEntityBase, GetEntityByNameMixin): self._client.request(url=f"/volumes/{volume.id}", method="DELETE") return True - def resize(self, volume, size): - # type: (Union[Volume, BoundVolume], int) -> BoundAction + def resize(self, volume: Volume | BoundVolume, size: int) -> BoundAction: """Changes the size of a volume. Note that downsizing a volume is not possible. :param volume: :class:`BoundVolume ` or :class:`Volume ` @@ -339,8 +387,12 @@ class VolumesClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundAction(self._client.actions, data["action"]) - def attach(self, volume, server, automount=None): - # type: (Union[Volume, BoundVolume], Union[Server, BoundServer], Optional[bool]) -> BoundAction + def attach( + self, + volume: Volume | BoundVolume, + server: Server | BoundServer, + automount: bool | None = None, + ) -> BoundAction: """Attaches a volume to a server. Works only if the server is in the same location as the volume. :param volume: :class:`BoundVolume ` or :class:`Volume ` @@ -348,7 +400,7 @@ class VolumesClient(ClientEntityBase, GetEntityByNameMixin): :param automount: boolean :return: :class:`BoundAction ` """ - data = {"server": server.id} + data: dict[str, Any] = {"server": server.id} if automount is not None: data["automount"] = automount @@ -359,8 +411,7 @@ class VolumesClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundAction(self._client.actions, data["action"]) - def detach(self, volume): - # type: (Union[Volume, BoundVolume]) -> BoundAction + def detach(self, volume: Volume | BoundVolume) -> BoundAction: """Detaches a volume from the server it’s attached to. You may attach it to a server again at a later time. :param volume: :class:`BoundVolume ` or :class:`Volume ` @@ -372,8 +423,11 @@ class VolumesClient(ClientEntityBase, GetEntityByNameMixin): ) return BoundAction(self._client.actions, data["action"]) - def change_protection(self, volume, delete=None): - # type: (Union[Volume, BoundVolume], Optional[bool], Optional[bool]) -> BoundAction + def change_protection( + self, + volume: Volume | BoundVolume, + delete: bool | None = None, + ) -> BoundAction: """Changes the protection configuration of a volume. :param volume: :class:`BoundVolume ` or :class:`Volume ` @@ -381,14 +435,12 @@ class VolumesClient(ClientEntityBase, GetEntityByNameMixin): If True, prevents the volume from being deleted :return: :class:`BoundAction ` """ - data = {} + data: dict[str, Any] = {} if delete is not None: data.update({"delete": delete}) response = self._client.request( - url="/volumes/{volume_id}/actions/change_protection".format( - volume_id=volume.id - ), + url=f"/volumes/{volume.id}/actions/change_protection", method="POST", json=data, ) diff --git a/plugins/module_utils/vendor/hcloud/volumes/domain.py b/plugins/module_utils/vendor/hcloud/volumes/domain.py index 1185cf4..7eb5440 100644 --- a/plugins/module_utils/vendor/hcloud/volumes/domain.py +++ b/plugins/module_utils/vendor/hcloud/volumes/domain.py @@ -1,9 +1,19 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + try: from dateutil.parser import isoparse except ImportError: isoparse = None -from ..core.domain import BaseDomain, DomainIdentityMixin +from ..core import BaseDomain, DomainIdentityMixin + +if TYPE_CHECKING: + from ..actions import BoundAction + from ..locations import BoundLocation, Location + from ..servers import BoundServer, Server + from .client import BoundVolume class Volume(BaseDomain, DomainIdentityMixin): @@ -54,17 +64,17 @@ class Volume(BaseDomain, DomainIdentityMixin): def __init__( self, - id, - name=None, - server=None, - created=None, - location=None, - size=None, - linux_device=None, - format=None, - protection=None, - labels=None, - status=None, + id: int, + name: str | None = None, + server: Server | BoundServer | None = None, + created: str | None = None, + location: Location | BoundLocation | None = None, + size: int | None = None, + linux_device: str | None = None, + format: str | None = None, + protection: dict | None = None, + labels: dict[str, str] | None = None, + status: str | None = None, ): self.id = id self.name = name @@ -94,9 +104,9 @@ class CreateVolumeResponse(BaseDomain): def __init__( self, - volume, # type: BoundVolume - action, # type: BoundAction - next_actions, # type: List[BoundAction] + volume: BoundVolume, + action: BoundAction, + next_actions: list[BoundAction], ): self.volume = volume self.action = action diff --git a/scripts/vendor.py b/scripts/vendor.py index e63d6ec..116a736 100755 --- a/scripts/vendor.py +++ b/scripts/vendor.py @@ -19,7 +19,7 @@ from textwrap import dedent logger = logging.getLogger("vendor") HCLOUD_SOURCE_URL = "https://github.com/hetznercloud/hcloud-python" -HCLOUD_VERSION = "v1.26.0" +HCLOUD_VERSION = "v1.27.1" HCLOUD_VENDOR_PATH = "plugins/module_utils/vendor/hcloud"