From bc61715c92fe9184700398a130adb20cfd4879b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20T=C3=B6lle?= Date: Wed, 10 Dec 2025 12:36:04 +0100 Subject: [PATCH] chore(deps): update dependency hcloud to v2.12.0 (#760) ##### SUMMARY Bump `hcloud-python` to [v2.12.0](https://github.com/hetznercloud/hcloud-python/releases/tag/v2.12.0) --- plugins/module_utils/vendor/hcloud/_client.py | 26 + .../module_utils/vendor/hcloud/_version.py | 2 +- .../hcloud/storage_box_types/__init__.py | 15 + .../vendor/hcloud/storage_box_types/client.py | 124 ++ .../vendor/hcloud/storage_box_types/domain.py | 49 + .../vendor/hcloud/storage_boxes/__init__.py | 55 + .../vendor/hcloud/storage_boxes/client.py | 1790 +++++++++++++++++ .../vendor/hcloud/storage_boxes/domain.py | 473 +++++ .../vendor/hcloud/zones/client.py | 55 + scripts/vendor.py | 2 +- 10 files changed, 2589 insertions(+), 2 deletions(-) create mode 100644 plugins/module_utils/vendor/hcloud/storage_box_types/__init__.py create mode 100644 plugins/module_utils/vendor/hcloud/storage_box_types/client.py create mode 100644 plugins/module_utils/vendor/hcloud/storage_box_types/domain.py create mode 100644 plugins/module_utils/vendor/hcloud/storage_boxes/__init__.py create mode 100644 plugins/module_utils/vendor/hcloud/storage_boxes/client.py create mode 100644 plugins/module_utils/vendor/hcloud/storage_boxes/domain.py diff --git a/plugins/module_utils/vendor/hcloud/_client.py b/plugins/module_utils/vendor/hcloud/_client.py index c2c6fde..e552433 100644 --- a/plugins/module_utils/vendor/hcloud/_client.py +++ b/plugins/module_utils/vendor/hcloud/_client.py @@ -28,6 +28,8 @@ from .primary_ips import PrimaryIPsClient from .server_types import ServerTypesClient from .servers import ServersClient from .ssh_keys import SSHKeysClient +from .storage_box_types import StorageBoxTypesClient +from .storage_boxes import StorageBoxesClient from .volumes import VolumesClient from .zones import ZonesClient @@ -144,11 +146,14 @@ class Client: poll_interval: int | float | BackoffFunction = 1.0, poll_max_retries: int = 120, timeout: float | tuple[float, float] | None = None, + *, + api_endpoint_hetzner: str = "https://api.hetzner.com/v1", ): """Create a new Client instance :param token: Hetzner Cloud API token :param api_endpoint: Hetzner Cloud API endpoint + :param api_endpoint_hetzner: Hetzner API endpoint. :param application_name: Your application name :param application_version: Your application _version :param poll_interval: @@ -167,6 +172,15 @@ class Client: poll_max_retries=poll_max_retries, timeout=timeout, ) + self._client_hetzner = ClientBase( + token=token, + endpoint=api_endpoint_hetzner, + application_name=application_name, + application_version=application_version, + poll_interval=poll_interval, + poll_max_retries=poll_max_retries, + timeout=timeout, + ) self.datacenters = DatacentersClient(self) """DatacentersClient Instance @@ -264,6 +278,18 @@ class Client: :type: :class:`ZonesClient ` """ + self.storage_box_types = StorageBoxTypesClient(self) + """StorageBoxTypesClient Instance + + :type: :class:`StorageBoxTypesClient ` + """ + + self.storage_boxes = StorageBoxesClient(self) + """StorageBoxesClient Instance + + :type: :class:`StorageBoxesClient ` + """ + def request( # type: ignore[no-untyped-def] self, method: str, diff --git a/plugins/module_utils/vendor/hcloud/_version.py b/plugins/module_utils/vendor/hcloud/_version.py index 25bb832..77282e5 100644 --- a/plugins/module_utils/vendor/hcloud/_version.py +++ b/plugins/module_utils/vendor/hcloud/_version.py @@ -1,3 +1,3 @@ from __future__ import annotations -__version__ = "2.11.1" # x-releaser-pleaser-version +__version__ = "2.12.0" # x-releaser-pleaser-version diff --git a/plugins/module_utils/vendor/hcloud/storage_box_types/__init__.py b/plugins/module_utils/vendor/hcloud/storage_box_types/__init__.py new file mode 100644 index 0000000..28d832b --- /dev/null +++ b/plugins/module_utils/vendor/hcloud/storage_box_types/__init__.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +from .client import ( + BoundStorageBoxType, + StorageBoxTypesClient, + StorageBoxTypesPageResult, +) +from .domain import StorageBoxType + +__all__ = [ + "BoundStorageBoxType", + "StorageBoxType", + "StorageBoxTypesClient", + "StorageBoxTypesPageResult", +] diff --git a/plugins/module_utils/vendor/hcloud/storage_box_types/client.py b/plugins/module_utils/vendor/hcloud/storage_box_types/client.py new file mode 100644 index 0000000..05cb03e --- /dev/null +++ b/plugins/module_utils/vendor/hcloud/storage_box_types/client.py @@ -0,0 +1,124 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..core import BoundModelBase, Meta, ResourceClientBase +from .domain import StorageBoxType + +if TYPE_CHECKING: + from .._client import Client + + +class BoundStorageBoxType(BoundModelBase, StorageBoxType): + _client: StorageBoxTypesClient + + model = StorageBoxType + + +class StorageBoxTypesPageResult(NamedTuple): + storage_box_types: list[BoundStorageBoxType] + meta: Meta + + +class StorageBoxTypesClient(ResourceClientBase): + """ + A client for the Storage Box Types API. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-types. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + + _base_url = "/storage_box_types" + + def __init__(self, client: Client): + super().__init__(client) + self._client = client._client_hetzner + + def get_by_id(self, id: int) -> BoundStorageBoxType: + """ + Returns a specific Storage Box Type. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-types-get-a-storage-box-type + + :param id: ID of the Storage Box Type. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + response = self._client.request( + method="GET", + url=f"{self._base_url}/{id}", + ) + return BoundStorageBoxType(self, response["storage_box_type"]) + + def get_by_name(self, name: str) -> BoundStorageBoxType | None: + """ + Returns a specific Storage Box Type. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-types-list-storage-box-types + + :param name: Name of the Storage Box Type. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._get_first_by(self.get_list, name=name) + + def get_list( + self, + name: str | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> StorageBoxTypesPageResult: + """ + Returns a list of Storage Box Types for a specific page. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-types-list-storage-box-types + + :param name: Name of the Storage Box Type. + :param page: Page number to return. + :param per_page: Maximum number of entries returned per page. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + params: dict[str, Any] = {} + if name is not None: + params["name"] = name + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + + response = self._client.request( + method="GET", + url=f"{self._base_url}", + params=params, + ) + return StorageBoxTypesPageResult( + storage_box_types=[ + BoundStorageBoxType(self, o) for o in response["storage_box_types"] + ], + meta=Meta.parse_meta(response), + ) + + def get_all( + self, + name: str | None = None, + ) -> list[BoundStorageBoxType]: + """ + Returns all Storage Box Types. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-types-list-storage-box-types + + :param name: Name of the Storage Box Type. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._iter_pages( + self.get_list, + name=name, + ) diff --git a/plugins/module_utils/vendor/hcloud/storage_box_types/domain.py b/plugins/module_utils/vendor/hcloud/storage_box_types/domain.py new file mode 100644 index 0000000..b807ce2 --- /dev/null +++ b/plugins/module_utils/vendor/hcloud/storage_box_types/domain.py @@ -0,0 +1,49 @@ +from __future__ import annotations + +from ..core import BaseDomain, DomainIdentityMixin +from ..deprecation import DeprecationInfo + + +class StorageBoxType(BaseDomain, DomainIdentityMixin): + """ + Storage Box Type Domain. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-types. + """ + + __api_properties__ = ( + "id", + "name", + "description", + "snapshot_limit", + "automatic_snapshot_limit", + "subaccounts_limit", + "size", + "deprecation", + "prices", + ) + __slots__ = __api_properties__ + + def __init__( + self, + id: int | None = None, + name: str | None = None, + description: str | None = None, + snapshot_limit: int | None = None, + automatic_snapshot_limit: int | None = None, + subaccounts_limit: int | None = None, + size: int | None = None, + prices: list[dict] | None = None, + deprecation: dict | None = None, + ): + self.id = id + self.name = name + self.description = description + self.snapshot_limit = snapshot_limit + self.automatic_snapshot_limit = automatic_snapshot_limit + self.subaccounts_limit = subaccounts_limit + self.size = size + self.prices = prices + self.deprecation = ( + DeprecationInfo.from_dict(deprecation) if deprecation is not None else None + ) diff --git a/plugins/module_utils/vendor/hcloud/storage_boxes/__init__.py b/plugins/module_utils/vendor/hcloud/storage_boxes/__init__.py new file mode 100644 index 0000000..4eb2272 --- /dev/null +++ b/plugins/module_utils/vendor/hcloud/storage_boxes/__init__.py @@ -0,0 +1,55 @@ +from __future__ import annotations + +from .client import ( + BoundStorageBox, + BoundStorageBoxSnapshot, + BoundStorageBoxSubaccount, + StorageBoxesClient, + StorageBoxesPageResult, + StorageBoxSnapshotsPageResult, + StorageBoxSubaccountsPageResult, +) +from .domain import ( + CreateStorageBoxResponse, + CreateStorageBoxSnapshotResponse, + CreateStorageBoxSubaccountResponse, + DeleteStorageBoxResponse, + DeleteStorageBoxSnapshotResponse, + DeleteStorageBoxSubaccountResponse, + StorageBox, + StorageBoxAccessSettings, + StorageBoxFoldersResponse, + StorageBoxSnapshot, + StorageBoxSnapshotPlan, + StorageBoxSnapshotStats, + StorageBoxStats, + StorageBoxStatus, + StorageBoxSubaccount, + StorageBoxSubaccountAccessSettings, +) + +__all__ = [ + "BoundStorageBox", + "BoundStorageBoxSnapshot", + "BoundStorageBoxSubaccount", + "CreateStorageBoxResponse", + "CreateStorageBoxSnapshotResponse", + "CreateStorageBoxSubaccountResponse", + "DeleteStorageBoxResponse", + "DeleteStorageBoxSnapshotResponse", + "DeleteStorageBoxSubaccountResponse", + "StorageBox", + "StorageBoxAccessSettings", + "StorageBoxesClient", + "StorageBoxesPageResult", + "StorageBoxFoldersResponse", + "StorageBoxSnapshot", + "StorageBoxSnapshotPlan", + "StorageBoxSnapshotsPageResult", + "StorageBoxSnapshotStats", + "StorageBoxStats", + "StorageBoxStatus", + "StorageBoxSubaccount", + "StorageBoxSubaccountAccessSettings", + "StorageBoxSubaccountsPageResult", +] diff --git a/plugins/module_utils/vendor/hcloud/storage_boxes/client.py b/plugins/module_utils/vendor/hcloud/storage_boxes/client.py new file mode 100644 index 0000000..0cb3c2d --- /dev/null +++ b/plugins/module_utils/vendor/hcloud/storage_boxes/client.py @@ -0,0 +1,1790 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..actions import ActionsPageResult, BoundAction, ResourceActionsClient +from ..core import BoundModelBase, Meta, ResourceClientBase +from ..locations import BoundLocation, Location +from ..ssh_keys import BoundSSHKey, SSHKey +from ..storage_box_types import BoundStorageBoxType, StorageBoxType +from .domain import ( + CreateStorageBoxResponse, + CreateStorageBoxSnapshotResponse, + CreateStorageBoxSubaccountResponse, + DeleteStorageBoxResponse, + DeleteStorageBoxSnapshotResponse, + DeleteStorageBoxSubaccountResponse, + StorageBox, + StorageBoxAccessSettings, + StorageBoxFoldersResponse, + StorageBoxSnapshot, + StorageBoxSnapshotPlan, + StorageBoxSnapshotStats, + StorageBoxStats, + StorageBoxSubaccount, + StorageBoxSubaccountAccessSettings, +) + +if TYPE_CHECKING: + from .._client import Client + + +class BoundStorageBox(BoundModelBase, StorageBox): + _client: StorageBoxesClient + + model = StorageBox + + def __init__( + self, + client: StorageBoxesClient, + data: dict[str, Any], + complete: bool = True, + ): + raw = data.get("storage_box_type") + if raw is not None: + data["storage_box_type"] = BoundStorageBoxType( + client._parent.storage_box_types, raw + ) + + raw = data.get("location") + if raw is not None: + data["location"] = BoundLocation(client._parent.locations, raw) + + raw = data.get("snapshot_plan") + if raw is not None: + data["snapshot_plan"] = StorageBoxSnapshotPlan.from_dict(raw) + + raw = data.get("access_settings") + if raw is not None: + data["access_settings"] = StorageBoxAccessSettings.from_dict(raw) + + raw = data.get("stats") + if raw is not None: + data["stats"] = StorageBoxStats.from_dict(raw) + + super().__init__(client, data, complete) + + 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 a paginated list of Actions for a Storage Box for a specific page. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-actions-list-actions-for-a-storage-box + + :param status: Filter the actions by status. The response will only contain actions matching the specified statuses. + :param sort: Sort resources by field and direction. + :param page: Page number to return. + :param per_page: Maximum number of entries returned per page. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.get_actions_list( + self, + status=status, + sort=sort, + page=page, + per_page=per_page, + ) + + def get_actions( + self, + *, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: + """ + Returns all Actions for a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-actions-list-actions-for-a-storage-box + + :param status: Filter the actions by status. The response will only contain actions matching the specified statuses. + :param sort: Sort resources by field and direction. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.get_actions( + self, + status=status, + sort=sort, + ) + + def update( + self, + *, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundStorageBox: + """ + Updates a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-update-a-storage-box + + :param name: Name of the Storage Box. + :param labels: User-defined labels (key/value pairs) for the Storage Box. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.update( + self, + name=name, + labels=labels, + ) + + def delete(self) -> DeleteStorageBoxResponse: + """ + Deletes a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-delete-a-storage-box + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.delete(self) + + def get_folders( + self, + *, + path: str | None = None, + ) -> StorageBoxFoldersResponse: + """ + Lists the (sub)folders contained in a Storage Box. + + Files are not part of the response. + + See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-list-folders-of-a-storage-box + + :param path: Relative path to list the folders from. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.get_folders( + self, + path=path, + ) + + def change_protection( + self, + *, + delete: bool | None = None, + ) -> BoundAction: + """ + Changes the protection of a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-actions-change-protection + + :param delete: Prevents the Storage Box from being deleted. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.change_protection( + self, + delete=delete, + ) + + def change_type( + self, + storage_box_type: StorageBoxType | BoundStorageBoxType, + ) -> BoundAction: + """ + Changes the type of a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-actions-change-type + + :param storage_box_type: Storage Box Type to change to. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.change_type( + self, + storage_box_type=storage_box_type, + ) + + def reset_password( + self, + password: str, + ) -> BoundAction: + """ + Reset the password of a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-actions-reset-password + + :param password: New password. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.reset_password( + self, + password=password, + ) + + def update_access_settings( + self, + access_settings: StorageBoxAccessSettings, + ) -> BoundAction: + """ + Update the access settings of a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-actions-update-access-settings + + :param access_settings: New access settings for the Storage Box. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.update_access_settings( + self, + access_settings=access_settings, + ) + + def rollback_snapshot( + self, + snapshot: StorageBoxSnapshot | BoundStorageBoxSnapshot, + ) -> BoundAction: + """ + Rollback the Storage Box to the given snapshot. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-actions-rollback-snapshot + + :param snapshot: Snapshot to rollback to. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.rollback_snapshot( + self, + snapshot=snapshot, + ) + + def disable_snapshot_plan( + self, + ) -> BoundAction: + """ + Disable the snapshot plan of a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-actions-disable-snapshot-plan + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.disable_snapshot_plan(self) + + def enable_snapshot_plan( + self, + snapshot_plan: StorageBoxSnapshotPlan, + ) -> BoundAction: + """ + Enable the snapshot plan of a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-actions-enable-snapshot-plan + + :param snapshot_plan: Snapshot Plan to enable. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.enable_snapshot_plan( + self, + snapshot_plan=snapshot_plan, + ) + + # Snapshots + ########################################################################### + + def get_snapshot_by_id( + self, + id: int, + ) -> BoundStorageBoxSnapshot: + """ + Returns a single Snapshot from a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-snapshots-get-a-snapshot + + :param id: ID of the Snapshot. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.get_snapshot_by_id(self, id=id) + + def get_snapshot_by_name( + self, + name: str, + ) -> BoundStorageBoxSnapshot: + """ + Returns a single Snapshot from a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-snapshots-list-snapshots + + :param name: Name of the Snapshot. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.get_snapshot_by_name(self, name=name) + + def get_snapshot_list( + self, + *, + name: str | None = None, + is_automatic: bool | None = None, + label_selector: str | None = None, + sort: list[str] | None = None, + ) -> StorageBoxSnapshotsPageResult: + """ + Returns all Snapshots for a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-snapshots-list-snapshots + + :param name: Filter resources by their name. The response will only contain the resources matching exactly the specified name. + :param is_automatic: Filter wether the snapshot was made by a Snapshot Plan. + :param label_selector: Filter resources by labels. The response will only contain resources matching the label selector. + :param sort: Sort resources by field and direction. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.get_snapshot_list( + self, + name=name, + is_automatic=is_automatic, + label_selector=label_selector, + sort=sort, + ) + + def get_snapshot_all( + self, + *, + name: str | None = None, + is_automatic: bool | None = None, + label_selector: str | None = None, + sort: list[str] | None = None, + ) -> list[BoundStorageBoxSnapshot]: + """ + Returns all Snapshots for a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-snapshots-list-snapshots + + :param name: Filter resources by their name. The response will only contain the resources matching exactly the specified name. + :param is_automatic: Filter whether the snapshot was made by a Snapshot Plan. + :param label_selector: Filter resources by labels. The response will only contain resources matching the label selector. + :param sort: Sort resources by field and direction. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.get_snapshot_all( + self, + name=name, + is_automatic=is_automatic, + label_selector=label_selector, + sort=sort, + ) + + def create_snapshot( + self, + *, + description: str | None = None, + labels: dict[str, str] | None = None, + ) -> CreateStorageBoxSnapshotResponse: + """ + Creates a Snapshot of the Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-snapshots-create-a-snapshot + + :param description: Description of the Snapshot. + :param labels: User-defined labels (key/value pairs) for the Snapshot. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.create_snapshot( + self, + description=description, + labels=labels, + ) + + # Subaccounts + ########################################################################### + + def get_subaccount_by_id( + self, + id: int, + ) -> BoundStorageBoxSubaccount: + """ + Returns a single Subaccount from a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-get-a-subaccount + + :param id: ID of the Subaccount. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.get_subaccount_by_id(self, id=id) + + def get_subaccount_by_username( + self, + username: str, + ) -> BoundStorageBoxSubaccount: + """ + Returns a single Subaccount from a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-list-subaccounts + + :param username: User name of the Subaccount. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.get_subaccount_by_username(self, username=username) + + def get_subaccount_list( + self, + *, + username: str | None = None, + label_selector: str | None = None, + sort: list[str] | None = None, + ) -> StorageBoxSubaccountsPageResult: + """ + Returns all Subaccounts for a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-list-subaccounts + + :param username: Filter resources by their username. The response will only contain the resources matching exactly the specified username. + :param label_selector: Filter resources by labels. The response will only contain resources matching the label selector. + :param sort: Sort resources by field and direction. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.get_subaccount_list( + self, + username=username, + label_selector=label_selector, + sort=sort, + ) + + def get_subaccount_all( + self, + *, + username: str | None = None, + label_selector: str | None = None, + sort: list[str] | None = None, + ) -> list[BoundStorageBoxSubaccount]: + """ + Returns all Subaccounts for a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-list-subaccounts + + :param username: Filter resources by their username. The response will only contain the resources matching exactly the specified username. + :param label_selector: Filter resources by labels. The response will only contain resources matching the label selector. + :param sort: Sort resources by field and direction. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.get_subaccount_all( + self, + username=username, + label_selector=label_selector, + sort=sort, + ) + + def create_subaccount( + self, + *, + home_directory: str, + password: str, + access_settings: StorageBoxSubaccountAccessSettings | None = None, + description: str | None = None, + labels: dict[str, str] | None = None, + ) -> CreateStorageBoxSubaccountResponse: + """ + Creates a Subaccount for the Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-create-a-subaccount + + :param storage_box: Storage Box to create a Subaccount for. + :param home_directory: Home directory of the Subaccount. + :param password: Password of the Subaccount. + :param access_settings: Access settings of the Subaccount. + :param description: Description of the Subaccount. + :param labels: User-defined labels (key/value pairs) for the Subaccount. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.create_subaccount( + self, + home_directory=home_directory, + password=password, + access_settings=access_settings, + description=description, + labels=labels, + ) + + +class BoundStorageBoxSnapshot(BoundModelBase, StorageBoxSnapshot): + _client: StorageBoxesClient + + model = StorageBoxSnapshot + + def __init__( + self, + client: StorageBoxesClient, + data: dict[str, Any], + complete: bool = True, + ): + raw = data.get("storage_box") + if raw is not None: + data["storage_box"] = BoundStorageBox( + client, data={"id": raw}, complete=False + ) + + raw = data.get("stats") + if raw is not None: + data["stats"] = StorageBoxSnapshotStats.from_dict(raw) + + super().__init__(client, data, complete) + + def _get_self(self) -> BoundStorageBoxSnapshot: + return self._client.get_snapshot_by_id( + self.data_model.storage_box, + self.data_model.id, + ) + + def update( + self, + *, + description: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundStorageBoxSnapshot: + """ + Updates a Storage Box Snapshot. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-snapshots-update-a-snapshot + + :param description: Description of the Snapshot. + :param labels: User-defined labels (key/value pairs) for the Snapshot. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.update_snapshot( + self, + description=description, + labels=labels, + ) + + def delete( + self, + ) -> DeleteStorageBoxSnapshotResponse: + """ + Deletes a Storage Box Snapshot. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-snapshots-delete-a-snapshot + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.delete_snapshot(self) + + +class BoundStorageBoxSubaccount(BoundModelBase, StorageBoxSubaccount): + _client: StorageBoxesClient + + model = StorageBoxSubaccount + + def __init__( + self, + client: StorageBoxesClient, + data: dict[str, Any], + complete: bool = True, + ): + raw = data.get("storage_box") + if raw is not None: + data["storage_box"] = BoundStorageBox( + client, data={"id": raw}, complete=False + ) + + raw = data.get("access_settings") + if raw is not None: + data["access_settings"] = StorageBoxSubaccountAccessSettings.from_dict(raw) + + super().__init__(client, data, complete) + + def _get_self(self) -> BoundStorageBoxSubaccount: + return self._client.get_subaccount_by_id( + self.data_model.storage_box, + self.data_model.id, + ) + + def update( + self, + *, + description: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundStorageBoxSubaccount: + """ + Updates a Storage Box Subaccount. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-update-a-subaccount + + :param description: Description of the Subaccount. + :param labels: User-defined labels (key/value pairs) for the Subaccount. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.update_subaccount( + self, + description=description, + labels=labels, + ) + + def delete( + self, + ) -> DeleteStorageBoxSubaccountResponse: + """ + Deletes a Storage Box Subaccount. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-delete-a-subaccount + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.delete_subaccount(self) + + def change_home_directory( + self, + home_directory: str, + ) -> BoundAction: + """ + Change the home directory of a Storage Box Subaccount. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccount-actions-change-home-directory + + :param home_directory: Home directory for the Subaccount. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.change_subaccount_home_directory( + self, home_directory=home_directory + ) + + def reset_password( + self, + password: str, + ) -> BoundAction: + """ + Reset the password of a Storage Box Subaccount. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccount-actions-reset-password + + :param password: Password for the Subaccount. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.reset_subaccount_password(self, password=password) + + def update_access_settings( + self, + access_settings: StorageBoxSubaccountAccessSettings, + ) -> BoundAction: + """ + Update the access settings of a Storage Box Subaccount. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccount-actions-update-access-settings + + :param access_settings: Access settings for the Subaccount. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._client.update_subaccount_access_settings( + self, + access_settings=access_settings, + ) + + +class StorageBoxesPageResult(NamedTuple): + storage_boxes: list[BoundStorageBox] + meta: Meta + + +class StorageBoxSnapshotsPageResult(NamedTuple): + snapshots: list[BoundStorageBoxSnapshot] + meta: Meta + + +class StorageBoxSubaccountsPageResult(NamedTuple): + subaccounts: list[BoundStorageBoxSubaccount] + meta: Meta + + +class StorageBoxesClient(ResourceClientBase): + """ + A client for the Storage Boxes API. + + See https://docs.hetzner.cloud/reference/hetzner#storage-boxes. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + + _base_url = "/storage_boxes" + + actions: ResourceActionsClient + """Storage Boxes scoped actions client + + :type: :class:`ResourceActionsClient ` + """ + + def __init__(self, client: Client): + super().__init__(client) + self._client = client._client_hetzner + self.actions = ResourceActionsClient(self, self._base_url) + + def get_by_id(self, id: int) -> BoundStorageBox: + """ + Returns a specific Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-get-a-storage-box + + :param id: ID of the Storage Box. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + response = self._client.request( + method="GET", + url=f"{self._base_url}/{id}", + ) + return BoundStorageBox(self, response["storage_box"]) + + def get_by_name(self, name: str) -> BoundStorageBox | None: + """ + Returns a specific Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-list-storage-boxes + + :param name: Name of the Storage Box. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._get_first_by(self.get_list, name=name) + + def get_list( + self, + *, + name: str | None = None, + label_selector: str | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> StorageBoxesPageResult: + """ + Returns a paginated list of Storage Boxes for a specific page. + + See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-list-storage-boxes + + :param name: Name of the Storage Box. + :param label_selector: Filter resources by labels. The response will only contain resources matching the label selector. + :param sort: Sort resources by field and direction. + :param page: Page number to return. + :param per_page: Maximum number of entries returned per page. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + params: dict[str, Any] = {} + if name is not None: + params["name"] = name + if label_selector is not None: + params["label_selector"] = label_selector + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + if sort is not None: + params["sort"] = sort + + response = self._client.request( + method="GET", + url=f"{self._base_url}", + params=params, + ) + return StorageBoxesPageResult( + storage_boxes=[BoundStorageBox(self, o) for o in response["storage_boxes"]], + meta=Meta.parse_meta(response), + ) + + def get_all( + self, + *, + name: str | None = None, + label_selector: str | None = None, + sort: list[str] | None = None, + ) -> list[BoundStorageBox]: + """ + Returns all Storage Boxes. + + See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-list-storage-boxes + + :param name: Name of the Storage Box. + :param label_selector: Filter resources by labels. The response will only contain resources matching the label selector. + :param sort: Sort resources by field and direction. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._iter_pages( + self.get_list, + name=name, + label_selector=label_selector, + sort=sort, + ) + + def create( + self, + *, + name: str, + password: str, + location: BoundLocation | Location, + storage_box_type: BoundStorageBoxType | StorageBoxType, + ssh_keys: list[str | SSHKey | BoundSSHKey] | None = None, + access_settings: StorageBoxAccessSettings | None = None, + labels: dict[str, str] | None = None, + ) -> CreateStorageBoxResponse: + """ + Creates a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-create-a-storage-box + + :param name: Name of the Storage Box. + :param password: Password of the Storage Box. + :param location: Location of the Storage Box. + :param storage_box_type: Type of the Storage Box. + :param ssh_keys: SSH public keys of the Storage Box. + :param access_settings: Access settings of the Storage Box. + :param labels: User-defined labels (key/value pairs) for the Storage Box. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + data: dict[str, Any] = { + "name": name, + "password": password, + "location": location.id_or_name, + "storage_box_type": storage_box_type.id_or_name, + } + if ssh_keys is not None: + data["ssh_keys"] = [ + o.public_key if isinstance(o, (SSHKey, BoundSSHKey)) else o + for o in ssh_keys + ] + if access_settings is not None: + data["access_settings"] = access_settings.to_payload() + if labels is not None: + data["labels"] = labels + + response = self._client.request( + method="POST", + url=f"{self._base_url}", + json=data, + ) + + return CreateStorageBoxResponse( + storage_box=BoundStorageBox(self, response["storage_box"]), + action=BoundAction(self._parent.actions, response["action"]), + ) + + def update( + self, + storage_box: BoundStorageBox | StorageBox, + *, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundStorageBox: + """ + Updates a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-update-a-storage-box + + :param storage_box: Storage Box to update. + :param name: Name of the Storage Box. + :param labels: User-defined labels (key/value pairs) for the Storage Box. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + data: dict[str, Any] = {} + if name is not None: + data["name"] = name + if labels is not None: + data["labels"] = labels + + response = self._client.request( + method="PUT", + url=f"{self._base_url}/{storage_box.id}", + json=data, + ) + + return BoundStorageBox(self, response["storage_box"]) + + def delete( + self, + storage_box: BoundStorageBox | StorageBox, + ) -> DeleteStorageBoxResponse: + """ + Deletes a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-delete-a-storage-box + + :param storage_box: Storage Box to delete. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + response = self._client.request( + method="DELETE", + url=f"{self._base_url}/{storage_box.id}", + ) + + return DeleteStorageBoxResponse( + action=BoundAction(self._parent.actions, response["action"]) + ) + + def get_folders( + self, + storage_box: BoundStorageBox | StorageBox, + *, + path: str | None = None, + ) -> StorageBoxFoldersResponse: + """ + Lists the (sub)folders contained in a Storage Box. + + Files are not part of the response. + + See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-list-folders-of-a-storage-box + + :param storage_box: Storage Box to list the folders from. + :param path: Relative path to list the folders from. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + params: dict[str, Any] = {} + if path is not None: + params["path"] = path + + response = self._client.request( + method="GET", + url=f"{self._base_url}/{storage_box.id}/folders", + params=params, + ) + + return StorageBoxFoldersResponse(folders=response["folders"]) + + def get_actions_list( + self, + storage_box: StorageBox | BoundStorageBox, + *, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: + """ + Returns a paginated list of Actions for a Storage Box for a specific page. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-actions-list-actions-for-a-storage-box + + :param storage_box: Storage Box to fetch the Actions from. + :param status: Filter the actions by status. The response will only contain actions matching the specified statuses. + :param sort: Sort resources by field and direction. + :param page: Page number to return. + :param per_page: Maximum number of entries returned per page. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + params: dict[str, Any] = {} + if status is not None: + params["status"] = status + if sort is not None: + params["sort"] = sort + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + + response = self._client.request( + method="GET", + url=f"/storage_boxes/{storage_box.id}/actions", + params=params, + ) + return ActionsPageResult( + actions=[BoundAction(self._parent.actions, o) for o in response["actions"]], + meta=Meta.parse_meta(response), + ) + + def get_actions( + self, + storage_box: StorageBox | BoundStorageBox, + *, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: + """ + Returns all Actions for a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-actions-list-actions-for-a-storage-box + + :param storage_box: Storage Box to fetch the Actions from. + :param status: Filter the actions by status. The response will only contain actions matching the specified statuses. + :param sort: Sort resources by field and direction. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._iter_pages( + self.get_actions_list, + storage_box, + status=status, + sort=sort, + ) + + def change_protection( + self, + storage_box: StorageBox | BoundStorageBox, + *, + delete: bool | None = None, + ) -> BoundAction: + """ + Changes the protection of a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-actions-change-protection + + :param storage_box: Storage Box to update. + :param delete: Prevents the Storage Box from being deleted. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + data: dict[str, Any] = {} + if delete is not None: + data["delete"] = delete + + response = self._client.request( + method="POST", + url=f"{self._base_url}/{storage_box.id}/actions/change_protection", + json=data, + ) + return BoundAction(self._parent.actions, response["action"]) + + def change_type( + self, + storage_box: StorageBox | BoundStorageBox, + storage_box_type: StorageBoxType | BoundStorageBoxType, + ) -> BoundAction: + """ + Changes the type of a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-actions-change-type + + :param storage_box: Storage Box to update. + :param storage_box_type: Storage Box Type to change to. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + data: dict[str, Any] = { + "storage_box_type": storage_box_type.id_or_name, + } + + response = self._client.request( + method="POST", + url=f"{self._base_url}/{storage_box.id}/actions/change_type", + json=data, + ) + return BoundAction(self._parent.actions, response["action"]) + + def reset_password( + self, + storage_box: StorageBox | BoundStorageBox, + password: str, + ) -> BoundAction: + """ + Reset the password of a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-actions-reset-password + + :param storage_box: Storage Box to update. + :param password: New password. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + data: dict[str, Any] = { + "password": password, + } + + response = self._client.request( + method="POST", + url=f"{self._base_url}/{storage_box.id}/actions/reset_password", + json=data, + ) + return BoundAction(self._parent.actions, response["action"]) + + def update_access_settings( + self, + storage_box: StorageBox | BoundStorageBox, + access_settings: StorageBoxAccessSettings, + ) -> BoundAction: + """ + Update the access settings of a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-actions-update-access-settings + + :param storage_box: Storage Box to update. + :param access_settings: New access settings for the Storage Box. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + data: dict[str, Any] = access_settings.to_payload() + + response = self._client.request( + method="POST", + url=f"{self._base_url}/{storage_box.id}/actions/update_access_settings", + json=data, + ) + return BoundAction(self._parent.actions, response["action"]) + + def rollback_snapshot( + self, + storage_box: StorageBox | BoundStorageBox, + snapshot: StorageBoxSnapshot | BoundStorageBoxSnapshot, + ) -> BoundAction: + """ + Rollback the Storage Box to the given snapshot. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-actions-rollback-snapshot + + :param storage_box: Storage Box to update. + :param snapshot: Snapshot to rollback to. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + data: dict[str, Any] = { + "snapshot": snapshot.id_or_name, + } + + response = self._client.request( + method="POST", + url=f"{self._base_url}/{storage_box.id}/actions/rollback_snapshot", + json=data, + ) + return BoundAction(self._parent.actions, response["action"]) + + def disable_snapshot_plan( + self, + storage_box: StorageBox | BoundStorageBox, + ) -> BoundAction: + """ + Disable the snapshot plan of a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-actions-disable-snapshot-plan + + :param storage_box: Storage Box to update. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + response = self._client.request( + method="POST", + url=f"{self._base_url}/{storage_box.id}/actions/disable_snapshot_plan", + ) + return BoundAction(self._parent.actions, response["action"]) + + def enable_snapshot_plan( + self, + storage_box: StorageBox | BoundStorageBox, + snapshot_plan: StorageBoxSnapshotPlan, + ) -> BoundAction: + """ + Enable the snapshot plan of a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-actions-enable-snapshot-plan + + :param storage_box: Storage Box to update. + :param snapshot_plan: Snapshot Plan to enable. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + data: dict[str, Any] = snapshot_plan.to_payload() + + response = self._client.request( + method="POST", + url=f"{self._base_url}/{storage_box.id}/actions/enable_snapshot_plan", + json=data, + ) + return BoundAction(self._parent.actions, response["action"]) + + # Snapshots + ########################################################################### + + def get_snapshot_by_id( + self, + storage_box: StorageBox | BoundStorageBox, + id: int, + ) -> BoundStorageBoxSnapshot: + """ + Returns a single Snapshot from a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-snapshots-get-a-snapshot + + :param storage_box: Storage Box to get the Snapshot from. + :param id: ID of the Snapshot. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + response = self._client.request( + method="GET", + url=f"{self._base_url}/{storage_box.id}/snapshots/{id}", + ) + return BoundStorageBoxSnapshot(self, response["snapshot"]) + + def get_snapshot_by_name( + self, + storage_box: StorageBox | BoundStorageBox, + name: str, + ) -> BoundStorageBoxSnapshot: + """ + Returns a single Snapshot from a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-snapshots-list-snapshots + + :param storage_box: Storage Box to get the Snapshot from. + :param name: Name of the Snapshot. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._get_first_by(self.get_snapshot_list, storage_box, name=name) + + def get_snapshot_list( + self, + storage_box: StorageBox | BoundStorageBox, + *, + name: str | None = None, + is_automatic: bool | None = None, + label_selector: str | None = None, + sort: list[str] | None = None, + ) -> StorageBoxSnapshotsPageResult: + """ + Returns all Snapshots for a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-snapshots-list-snapshots + + :param storage_box: Storage Box to get the Snapshots from. + :param name: Filter resources by their name. The response will only contain the resources matching exactly the specified name. + :param is_automatic: Filter whether the snapshot was made by a Snapshot Plan. + :param label_selector: Filter resources by labels. The response will only contain resources matching the label selector. + :param sort: Sort resources by field and direction. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + params: dict[str, Any] = {} + if name is not None: + params["name"] = name + if is_automatic is not None: + params["is_automatic"] = is_automatic + if label_selector is not None: + params["label_selector"] = label_selector + if sort is not None: + params["sort"] = sort + + response = self._client.request( + method="GET", + url=f"{self._base_url}/{storage_box.id}/snapshots", + params=params, + ) + return StorageBoxSnapshotsPageResult( + snapshots=[ + BoundStorageBoxSnapshot(self, item) for item in response["snapshots"] + ], + meta=Meta.parse_meta(response), + ) + + def get_snapshot_all( + self, + storage_box: StorageBox | BoundStorageBox, + *, + name: str | None = None, + is_automatic: bool | None = None, + label_selector: str | None = None, + sort: list[str] | None = None, + ) -> list[BoundStorageBoxSnapshot]: + """ + Returns all Snapshots for a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-snapshots-list-snapshots + + :param storage_box: Storage Box to get the Snapshots from. + :param name: Filter resources by their name. The response will only contain the resources matching exactly the specified name. + :param is_automatic: Filter whether the snapshot was made by a Snapshot Plan. + :param label_selector: Filter resources by labels. The response will only contain resources matching the label selector. + :param sort: Sort resources by field and direction. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + # The endpoint does not have pagination, forward to the list method. + result, _ = self.get_snapshot_list( + storage_box, + name=name, + is_automatic=is_automatic, + label_selector=label_selector, + sort=sort, + ) + return result + + def create_snapshot( + self, + storage_box: StorageBox | BoundStorageBox, + *, + description: str | None = None, + labels: dict[str, str] | None = None, + ) -> CreateStorageBoxSnapshotResponse: + """ + Creates a Snapshot of the Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-snapshots-create-a-snapshot + + :param storage_box: Storage Box to create a Snapshot from. + :param description: Description of the Snapshot. + :param labels: User-defined labels (key/value pairs) for the Snapshot. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + data: dict[str, Any] = {} + if description is not None: + data["description"] = description + if labels is not None: + data["labels"] = labels + + response = self._client.request( + method="POST", + url=f"{self._base_url}/{storage_box.id}/snapshots", + json=data, + ) + return CreateStorageBoxSnapshotResponse( + snapshot=BoundStorageBoxSnapshot( + self, + response["snapshot"], + # API only returns a partial object. + complete=False, + ), + action=BoundAction(self._parent.actions, response["action"]), + ) + + def update_snapshot( + self, + snapshot: StorageBoxSnapshot | BoundStorageBoxSnapshot, + *, + description: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundStorageBoxSnapshot: + """ + Updates a Storage Box Snapshot. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-snapshots-update-a-snapshot + + :param snapshot: Storage Box Snapshot to update. + :param description: Description of the Snapshot. + :param labels: User-defined labels (key/value pairs) for the Snapshot. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + if snapshot.storage_box is None: + raise ValueError("snapshot storage_box property is none") + + data: dict[str, Any] = {} + if description is not None: + data["description"] = description + if labels is not None: + data["labels"] = labels + + response = self._client.request( + method="PUT", + url=f"{self._base_url}/{snapshot.storage_box.id}/snapshots/{snapshot.id}", + json=data, + ) + return BoundStorageBoxSnapshot(self, response["snapshot"]) + + def delete_snapshot( + self, + snapshot: StorageBoxSnapshot | BoundStorageBoxSnapshot, + ) -> DeleteStorageBoxSnapshotResponse: + """ + Deletes a Storage Box Snapshot. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-snapshots-delete-a-snapshot + + :param snapshot: Storage Box Snapshot to delete. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + if snapshot.storage_box is None: + raise ValueError("snapshot storage_box property is none") + + response = self._client.request( + method="DELETE", + url=f"{self._base_url}/{snapshot.storage_box.id}/snapshots/{snapshot.id}", + ) + return DeleteStorageBoxSnapshotResponse( + action=BoundAction(self._parent.actions, response["action"]), + ) + + # Subaccounts + ########################################################################### + + def get_subaccount_by_id( + self, + storage_box: StorageBox | BoundStorageBox, + id: int, + ) -> BoundStorageBoxSubaccount: + """ + Returns a single Subaccount from a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-get-a-subaccount + + :param storage_box: Storage Box to get the Subaccount from. + :param id: ID of the Subaccount. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + response = self._client.request( + method="GET", + url=f"{self._base_url}/{storage_box.id}/subaccounts/{id}", + ) + return BoundStorageBoxSubaccount(self, response["subaccount"]) + + def get_subaccount_by_username( + self, + storage_box: StorageBox | BoundStorageBox, + username: str, + ) -> BoundStorageBoxSubaccount: + """ + Returns a single Subaccount from a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-list-subaccounts + + :param storage_box: Storage Box to get the Subaccount from. + :param username: User name of the Subaccount. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + return self._get_first_by( + self.get_subaccount_list, + storage_box, + username=username, + ) + + def get_subaccount_list( + self, + storage_box: StorageBox | BoundStorageBox, + *, + username: str | None = None, + label_selector: str | None = None, + sort: list[str] | None = None, + ) -> StorageBoxSubaccountsPageResult: + """ + Returns all Subaccounts for a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-list-subaccounts + + :param storage_box: Storage Box to get the Subaccount from. + :param username: Filter resources by their username. The response will only contain the resources matching exactly the specified username. + :param label_selector: Filter resources by labels. The response will only contain resources matching the label selector. + :param sort: Sort resources by field and direction. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + params: dict[str, Any] = {} + if username is not None: + params["username"] = username + if label_selector is not None: + params["label_selector"] = label_selector + if sort is not None: + params["sort"] = sort + + response = self._client.request( + method="GET", + url=f"{self._base_url}/{storage_box.id}/subaccounts", + params=params, + ) + return StorageBoxSubaccountsPageResult( + subaccounts=[ + BoundStorageBoxSubaccount(self, item) + for item in response["subaccounts"] + ], + meta=Meta.parse_meta(response), + ) + + def get_subaccount_all( + self, + storage_box: StorageBox | BoundStorageBox, + *, + username: str | None = None, + label_selector: str | None = None, + sort: list[str] | None = None, + ) -> list[BoundStorageBoxSubaccount]: + """ + Returns all Subaccounts for a Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-list-subaccounts + + :param storage_box: Storage Box to get the Subaccount from. + :param username: Filter resources by their username. The response will only contain the resources matching exactly the specified username. + :param label_selector: Filter resources by labels. The response will only contain resources matching the label selector. + :param sort: Sort resources by field and direction. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + # The endpoint does not have pagination, forward to the list method. + result, _ = self.get_subaccount_list( + storage_box, + username=username, + label_selector=label_selector, + sort=sort, + ) + return result + + def create_subaccount( + self, + storage_box: StorageBox | BoundStorageBox, + *, + home_directory: str, + password: str, + access_settings: StorageBoxSubaccountAccessSettings | None = None, + description: str | None = None, + labels: dict[str, str] | None = None, + ) -> CreateStorageBoxSubaccountResponse: + """ + Creates a Subaccount for the Storage Box. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-create-a-subaccount + + :param storage_box: Storage Box to create a Subaccount for. + :param home_directory: Home directory of the Subaccount. + :param password: Password of the Subaccount. + :param access_settings: Access settings of the Subaccount. + :param description: Description of the Subaccount. + :param labels: User-defined labels (key/value pairs) for the Subaccount. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + data: dict[str, Any] = { + "home_directory": home_directory, + "password": password, + } + if access_settings is not None: + data["access_settings"] = access_settings.to_payload() + if description is not None: + data["description"] = description + if labels is not None: + data["labels"] = labels + + response = self._client.request( + method="POST", + url=f"{self._base_url}/{storage_box.id}/subaccounts", + json=data, + ) + return CreateStorageBoxSubaccountResponse( + subaccount=BoundStorageBoxSubaccount( + self, + response["subaccount"], + # API only returns a partial object. + complete=False, + ), + action=BoundAction(self._parent.actions, response["action"]), + ) + + def update_subaccount( + self, + subaccount: StorageBoxSubaccount | BoundStorageBoxSubaccount, + *, + description: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundStorageBoxSubaccount: + """ + Updates a Storage Box Subaccount. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-update-a-subaccount + + :param subaccount: Storage Box Subaccount to update. + :param description: Description of the Subaccount. + :param labels: User-defined labels (key/value pairs) for the Subaccount. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + if subaccount.storage_box is None: + raise ValueError("subaccount storage_box property is none") + + data: dict[str, Any] = {} + if description is not None: + data["description"] = description + if labels is not None: + data["labels"] = labels + + response = self._client.request( + method="PUT", + url=f"{self._base_url}/{subaccount.storage_box.id}/subaccounts/{subaccount.id}", + json=data, + ) + return BoundStorageBoxSubaccount(self, response["subaccount"]) + + def delete_subaccount( + self, + subaccount: StorageBoxSubaccount | BoundStorageBoxSubaccount, + ) -> DeleteStorageBoxSubaccountResponse: + """ + Deletes a Storage Box Subaccount. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-delete-a-subaccount + + :param subaccount: Storage Box Subaccount to delete. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + if subaccount.storage_box is None: + raise ValueError("subaccount storage_box property is none") + + response = self._client.request( + method="DELETE", + url=f"{self._base_url}/{subaccount.storage_box.id}/subaccounts/{subaccount.id}", + ) + return DeleteStorageBoxSubaccountResponse( + action=BoundAction(self._parent.actions, response["action"]), + ) + + def change_subaccount_home_directory( + self, + subaccount: StorageBoxSubaccount | BoundStorageBoxSubaccount, + home_directory: str, + ) -> BoundAction: + """ + Change the home directory of a Storage Box Subaccount. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccount-actions-change-home-directory + + :param subaccount: Storage Box Subaccount to update. + :param home_directory: Home directory for the Subaccount. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + if subaccount.storage_box is None: + raise ValueError("subaccount storage_box property is none") + + data: dict[str, Any] = { + "home_directory": home_directory, + } + + response = self._client.request( + method="POST", + url=f"{self._base_url}/{subaccount.storage_box.id}/subaccounts/{subaccount.id}/actions/change_home_directory", + json=data, + ) + return BoundAction(self._parent.actions, response["action"]) + + def reset_subaccount_password( + self, + subaccount: StorageBoxSubaccount | BoundStorageBoxSubaccount, + password: str, + ) -> BoundAction: + """ + Reset the password of a Storage Box Subaccount. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccount-actions-reset-password + + :param subaccount: Storage Box Subaccount to update. + :param password: Password for the Subaccount. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + if subaccount.storage_box is None: + raise ValueError("subaccount storage_box property is none") + + data: dict[str, Any] = { + "password": password, + } + + response = self._client.request( + method="POST", + url=f"{self._base_url}/{subaccount.storage_box.id}/subaccounts/{subaccount.id}/actions/reset_subaccount_password", + json=data, + ) + return BoundAction(self._parent.actions, response["action"]) + + def update_subaccount_access_settings( + self, + subaccount: StorageBoxSubaccount | BoundStorageBoxSubaccount, + access_settings: StorageBoxSubaccountAccessSettings, + ) -> BoundAction: + """ + Update the access settings of a Storage Box Subaccount. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccount-actions-update-access-settings + + :param subaccount: Storage Box Subaccount to update. + :param access_settings: Access settings for the Subaccount. + + Experimental: + Storage Box support is experimental, breaking changes may occur within minor releases. + """ + if subaccount.storage_box is None: + raise ValueError("subaccount storage_box property is none") + + data: dict[str, Any] = access_settings.to_payload() + + response = self._client.request( + method="POST", + url=f"{self._base_url}/{subaccount.storage_box.id}/subaccounts/{subaccount.id}/actions/update_access_settings", + json=data, + ) + return BoundAction(self._parent.actions, response["action"]) diff --git a/plugins/module_utils/vendor/hcloud/storage_boxes/domain.py b/plugins/module_utils/vendor/hcloud/storage_boxes/domain.py new file mode 100644 index 0000000..040f6e8 --- /dev/null +++ b/plugins/module_utils/vendor/hcloud/storage_boxes/domain.py @@ -0,0 +1,473 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Literal + +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + +from ..actions import BoundAction +from ..core import BaseDomain, DomainIdentityMixin +from ..locations import BoundLocation, Location +from ..storage_box_types import BoundStorageBoxType, StorageBoxType + +if TYPE_CHECKING: + from .client import ( + BoundStorageBox, + BoundStorageBoxSnapshot, + BoundStorageBoxSubaccount, + ) + +StorageBoxStatus = Literal[ + "active", + "initializing", + "locked", +] + + +class StorageBox(BaseDomain, DomainIdentityMixin): + """ + Storage Box Domain. + + See https://docs.hetzner.cloud/reference/hetzner#storage-boxes. + """ + + STATUS_ACTIVE = "active" + STATUS_INITIALIZING = "initializing" + STATUS_LOCKED = "locked" + + __api_properties__ = ( + "id", + "name", + "storage_box_type", + "location", + "system", + "server", + "username", + "labels", + "protection", + "snapshot_plan", + "access_settings", + "stats", + "status", + "created", + ) + __slots__ = __api_properties__ + + def __init__( + self, + id: int | None = None, + name: str | None = None, + storage_box_type: BoundStorageBoxType | StorageBoxType | None = None, + location: BoundLocation | Location | None = None, + system: str | None = None, + server: str | None = None, + username: str | None = None, + labels: dict[str, str] | None = None, + protection: dict[str, bool] | None = None, + snapshot_plan: StorageBoxSnapshotPlan | None = None, + access_settings: StorageBoxAccessSettings | None = None, + stats: StorageBoxStats | None = None, + status: StorageBoxStatus | None = None, + created: str | None = None, + ): + self.id = id + self.name = name + self.storage_box_type = storage_box_type + self.location = location + self.system = system + self.server = server + self.username = username + self.labels = labels + self.protection = protection + self.snapshot_plan = snapshot_plan + self.access_settings = access_settings + self.stats = stats + self.status = status + self.created = isoparse(created) if created else None + + +class StorageBoxAccessSettings(BaseDomain): + """ + Storage Box Access Settings Domain. + """ + + __api_properties__ = ( + "reachable_externally", + "samba_enabled", + "ssh_enabled", + "webdav_enabled", + "zfs_enabled", + ) + __slots__ = __api_properties__ + + def __init__( + self, + reachable_externally: bool | None = None, + samba_enabled: bool | None = None, + ssh_enabled: bool | None = None, + webdav_enabled: bool | None = None, + zfs_enabled: bool | None = None, + ): + self.reachable_externally = reachable_externally + self.samba_enabled = samba_enabled + self.ssh_enabled = ssh_enabled + self.webdav_enabled = webdav_enabled + self.zfs_enabled = zfs_enabled + + def to_payload(self) -> dict[str, Any]: + """ + Generates the request payload from this domain object. + """ + payload: dict[str, Any] = {} + if self.reachable_externally is not None: + payload["reachable_externally"] = self.reachable_externally + if self.samba_enabled is not None: + payload["samba_enabled"] = self.samba_enabled + if self.ssh_enabled is not None: + payload["ssh_enabled"] = self.ssh_enabled + if self.webdav_enabled is not None: + payload["webdav_enabled"] = self.webdav_enabled + if self.zfs_enabled is not None: + payload["zfs_enabled"] = self.zfs_enabled + return payload + + +class StorageBoxStats(BaseDomain): + """ + Storage Box Stats Domain. + """ + + __api_properties__ = ( + "size", + "size_data", + "size_snapshots", + ) + __slots__ = __api_properties__ + + def __init__( + self, + size: int | None = None, + size_data: int | None = None, + size_snapshots: int | None = None, + ): + self.size = size + self.size_data = size_data + self.size_snapshots = size_snapshots + + +class StorageBoxSnapshotPlan(BaseDomain): + """ + Storage Box Snapshot Plan Domain. + """ + + __api_properties__ = ( + "max_snapshots", + "hour", + "minute", + "day_of_week", + "day_of_month", + ) + __slots__ = __api_properties__ + + def __init__( + self, + max_snapshots: int, + hour: int, + minute: int, + day_of_week: int | None = None, + day_of_month: int | None = None, + ): + self.max_snapshots = max_snapshots + self.hour = hour + self.minute = minute + self.day_of_week = day_of_week + self.day_of_month = day_of_month + + def to_payload(self) -> dict[str, Any]: + """ + Generates the request payload from this domain object. + """ + payload: dict[str, Any] = { + "max_snapshots": self.max_snapshots, + "hour": self.hour, + "minute": self.minute, + "day_of_week": self.day_of_week, # API default is null + "day_of_month": self.day_of_month, # API default is null + } + + return payload + + +class CreateStorageBoxResponse(BaseDomain): + """ + Create Storage Box Response Domain. + """ + + __api_properties__ = ( + "storage_box", + "action", + ) + __slots__ = __api_properties__ + + def __init__( + self, + storage_box: BoundStorageBox, + action: BoundAction, + ): + self.storage_box = storage_box + self.action = action + + +class DeleteStorageBoxResponse(BaseDomain): + """ + Delete Storage Box Response Domain. + """ + + __api_properties__ = ("action",) + __slots__ = __api_properties__ + + def __init__( + self, + action: BoundAction, + ): + self.action = action + + +class StorageBoxFoldersResponse(BaseDomain): + """ + Storage Box Folders Response Domain. + """ + + __api_properties__ = ("folders",) + __slots__ = __api_properties__ + + def __init__( + self, + folders: list[str], + ): + self.folders = folders + + +# Snapshots +############################################################################### + + +class StorageBoxSnapshot(BaseDomain, DomainIdentityMixin): + """ + Storage Box Snapshot Domain. + """ + + __api_properties__ = ( + "id", + "name", + "description", + "is_automatic", + "labels", + "storage_box", + "created", + "stats", + ) + __slots__ = __api_properties__ + + def __init__( + self, + id: int | None = None, + name: str | None = None, + description: str | None = None, + is_automatic: bool | None = None, + labels: dict[str, str] | None = None, + storage_box: BoundStorageBox | StorageBox | None = None, + created: str | None = None, + stats: StorageBoxSnapshotStats | None = None, + ): + self.id = id + self.name = name + self.description = description + self.is_automatic = is_automatic + self.labels = labels + self.storage_box = storage_box + self.created = isoparse(created) if created else None + self.stats = stats + + +class StorageBoxSnapshotStats(BaseDomain): + """ + Storage Box Snapshot Stats Domain. + """ + + __api_properties__ = ( + "size", + "size_filesystem", + ) + __slots__ = __api_properties__ + + def __init__( + self, + size: int, + size_filesystem: int, + ): + self.size = size + self.size_filesystem = size_filesystem + + +class CreateStorageBoxSnapshotResponse(BaseDomain): + """ + Create Storage Box Snapshot Response Domain. + """ + + __api_properties__ = ( + "snapshot", + "action", + ) + __slots__ = __api_properties__ + + def __init__( + self, + snapshot: BoundStorageBoxSnapshot, + action: BoundAction, + ): + self.snapshot = snapshot + self.action = action + + +class DeleteStorageBoxSnapshotResponse(BaseDomain): + """ + Delete Storage Box Snapshot Response Domain. + """ + + __api_properties__ = ("action",) + __slots__ = __api_properties__ + + def __init__( + self, + action: BoundAction, + ): + self.action = action + + +# Subaccounts +############################################################################### + + +class StorageBoxSubaccount(BaseDomain, DomainIdentityMixin): + """ + Storage Box Subaccount Domain. + """ + + __api_properties__ = ( + "id", + "username", + "description", + "server", + "home_directory", + "access_settings", + "labels", + "storage_box", + "created", + ) + __slots__ = __api_properties__ + + def __init__( + self, + id: int | None = None, + username: str | None = None, + description: str | None = None, + server: str | None = None, + home_directory: str | None = None, + access_settings: StorageBoxSubaccountAccessSettings | None = None, + labels: dict[str, str] | None = None, + storage_box: BoundStorageBox | StorageBox | None = None, + created: str | None = None, + ): + self.id = id + self.username = username + self.description = description + self.server = server + self.home_directory = home_directory + self.access_settings = access_settings + self.labels = labels + self.storage_box = storage_box + self.created = isoparse(created) if created else None + + +class StorageBoxSubaccountAccessSettings(BaseDomain): + """ + Storage Box Subaccount Access Settings Domain. + """ + + __api_properties__ = ( + "reachable_externally", + "samba_enabled", + "ssh_enabled", + "webdav_enabled", + "readonly", + ) + __slots__ = __api_properties__ + + def __init__( + self, + reachable_externally: bool | None = None, + samba_enabled: bool | None = None, + ssh_enabled: bool | None = None, + webdav_enabled: bool | None = None, + readonly: bool | None = None, + ): + self.reachable_externally = reachable_externally + self.samba_enabled = samba_enabled + self.ssh_enabled = ssh_enabled + self.webdav_enabled = webdav_enabled + self.readonly = readonly + + def to_payload(self) -> dict[str, Any]: + """ + Generates the request payload from this domain object. + """ + payload: dict[str, Any] = {} + if self.reachable_externally is not None: + payload["reachable_externally"] = self.reachable_externally + if self.samba_enabled is not None: + payload["samba_enabled"] = self.samba_enabled + if self.ssh_enabled is not None: + payload["ssh_enabled"] = self.ssh_enabled + if self.webdav_enabled is not None: + payload["webdav_enabled"] = self.webdav_enabled + if self.readonly is not None: + payload["readonly"] = self.readonly + return payload + + +class CreateStorageBoxSubaccountResponse(BaseDomain): + """ + Create Storage Box Subaccount Response Domain. + """ + + __api_properties__ = ( + "subaccount", + "action", + ) + __slots__ = __api_properties__ + + def __init__( + self, + subaccount: BoundStorageBoxSubaccount, + action: BoundAction, + ): + self.subaccount = subaccount + self.action = action + + +class DeleteStorageBoxSubaccountResponse(BaseDomain): + """ + Delete Storage Box Subaccount Response Domain. + """ + + __api_properties__ = ("action",) + __slots__ = __api_properties__ + + def __init__( + self, + action: BoundAction, + ): + self.action = action diff --git a/plugins/module_utils/vendor/hcloud/zones/client.py b/plugins/module_utils/vendor/hcloud/zones/client.py index 832d76f..b334604 100644 --- a/plugins/module_utils/vendor/hcloud/zones/client.py +++ b/plugins/module_utils/vendor/hcloud/zones/client.py @@ -359,6 +359,21 @@ class BoundZone(BoundModelBase, Zone): """ return self._client.add_rrset_records(rrset=rrset, records=records, ttl=ttl) + def update_rrset_records( + self, + rrset: ZoneRRSet | BoundZoneRRSet, + records: list[ZoneRecord], + ) -> BoundAction: + """ + Updates records in a ZoneRRSet. + + See https://docs.hetzner.cloud/reference/cloud#zone-rrset-actions-update-records-to-an-rrset + + :param rrset: RRSet to update. + :param records: Records to update in the RRSet. + """ + return self._client.update_rrset_records(rrset=rrset, records=records) + def remove_rrset_records( self, rrset: ZoneRRSet | BoundZoneRRSet, @@ -479,6 +494,19 @@ class BoundZoneRRSet(BoundModelBase, ZoneRRSet): """ return self._client.add_rrset_records(self, records=records, ttl=ttl) + def update_rrset_records( + self, + records: list[ZoneRecord], + ) -> BoundAction: + """ + Updates records in a ZoneRRSet. + + See https://docs.hetzner.cloud/reference/cloud#zone-rrset-actions-update-records-to-an-rrset + + :param records: Records to update in the RRSet. + """ + return self._client.update_rrset_records(self, records=records) + def remove_rrset_records( self, records: list[ZoneRecord], @@ -1172,6 +1200,33 @@ class ZonesClient(ResourceClientBase): ) return BoundAction(self._parent.actions, response["action"]) + def update_rrset_records( + self, + rrset: ZoneRRSet | BoundZoneRRSet, + records: list[ZoneRecord], + ) -> BoundAction: + """ + Updates records in a ZoneRRSet. + + See https://docs.hetzner.cloud/reference/cloud#zone-rrset-actions-update-records-to-an-rrset + + :param rrset: RRSet to update. + :param records: Records to update in the RRSet. + """ + if rrset.zone is None: + raise ValueError("rrset zone property is none") + + data: dict[str, Any] = { + "records": [o.to_payload() for o in records], + } + + response = self._client.request( + method="POST", + url=f"{self._base_url}/{rrset.zone.id_or_name}/rrsets/{rrset.name}/{rrset.type}/actions/update_records", + json=data, + ) + return BoundAction(self._parent.actions, response["action"]) + def remove_rrset_records( self, rrset: ZoneRRSet | BoundZoneRRSet, diff --git a/scripts/vendor.py b/scripts/vendor.py index 9a8f0d2..8e68e15 100755 --- a/scripts/vendor.py +++ b/scripts/vendor.py @@ -22,7 +22,7 @@ from textwrap import dedent logger = logging.getLogger("vendor") HCLOUD_SOURCE_URL = "https://github.com/hetznercloud/hcloud-python" -HCLOUD_VERSION = "v2.11.1" +HCLOUD_VERSION = "v2.12.0" HCLOUD_VENDOR_PATH = "plugins/module_utils/vendor/hcloud"