1
0
Fork 0
mirror of https://github.com/ansible-collections/hetzner.hcloud.git synced 2026-02-04 08:01:49 +00:00

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)
This commit is contained in:
Julian Tölle 2025-12-10 12:36:04 +01:00 committed by GitHub
parent ea973be048
commit bc61715c92
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 2589 additions and 2 deletions

View file

@ -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 <hcloud.zones.client.ZonesClient>`
"""
self.storage_box_types = StorageBoxTypesClient(self)
"""StorageBoxTypesClient Instance
:type: :class:`StorageBoxTypesClient <hcloud.storage_box_types.client.StorageBoxTypesClient>`
"""
self.storage_boxes = StorageBoxesClient(self)
"""StorageBoxesClient Instance
:type: :class:`StorageBoxesClient <hcloud.storage_boxes.client.StorageBoxesClient>`
"""
def request( # type: ignore[no-untyped-def]
self,
method: str,

View file

@ -1,3 +1,3 @@
from __future__ import annotations
__version__ = "2.11.1" # x-releaser-pleaser-version
__version__ = "2.12.0" # x-releaser-pleaser-version

View file

@ -0,0 +1,15 @@
from __future__ import annotations
from .client import (
BoundStorageBoxType,
StorageBoxTypesClient,
StorageBoxTypesPageResult,
)
from .domain import StorageBoxType
__all__ = [
"BoundStorageBoxType",
"StorageBoxType",
"StorageBoxTypesClient",
"StorageBoxTypesPageResult",
]

View file

@ -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,
)

View file

@ -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
)

View file

@ -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",
]

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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,

View file

@ -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"