Skip to content

File tree

9 files changed

+993
-0
lines changed

9 files changed

+993
-0
lines changed

docs/api.clients.storage_boxes.rst

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
StorageBoxesClient
2+
=====================
3+
4+
.. autoclass:: hcloud.storage_boxes.client.StorageBoxesClient
5+
:members:
6+
7+
.. autoclass:: hcloud.storage_boxes.client.BoundStorageBox
8+
:members:
9+
10+
.. autoclass:: hcloud.storage_boxes.client.StorageBox
11+
:members:
12+
13+
.. autoclass:: hcloud.storage_boxes.client.StorageBoxSnapshotPlan
14+
:members:
15+
16+
.. autoclass:: hcloud.storage_boxes.client.StorageBoxStats
17+
:members:
18+
19+
.. autoclass:: hcloud.storage_boxes.client.StorageBoxAccessSettings
20+
:members:
21+
22+
.. autoclass:: hcloud.storage_boxes.client.CreateStorageBoxResponse
23+
:members:
24+
25+
.. autoclass:: hcloud.storage_boxes.client.DeleteStorageBoxResponse
26+
:members:
27+
28+
.. autoclass:: hcloud.storage_boxes.client.StorageBoxFoldersResponse
29+
:members:

hcloud/_client.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from .servers import ServersClient
2727
from .ssh_keys import SSHKeysClient
2828
from .storage_box_types import StorageBoxTypesClient
29+
from .storage_boxes import StorageBoxesClient
2930
from .volumes import VolumesClient
3031
from .zones import ZonesClient
3132

@@ -280,6 +281,12 @@ def __init__(
280281
:type: :class:`StorageBoxTypesClient <hcloud.storage_box_types.client.StorageBoxTypesClient>`
281282
"""
282283

284+
self.storage_boxes = StorageBoxesClient(self)
285+
"""StorageBoxesClient Instance
286+
287+
:type: :class:`StorageBoxesClient <hcloud.storage_boxes.client.StorageBoxesClient>`
288+
"""
289+
283290
def request( # type: ignore[no-untyped-def]
284291
self,
285292
method: str,

hcloud/storage_boxes/__init__.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from __future__ import annotations
2+
3+
from .client import (
4+
BoundStorageBox,
5+
StorageBoxesClient,
6+
StorageBoxesPageResult,
7+
)
8+
from .domain import (
9+
CreateStorageBoxResponse,
10+
DeleteStorageBoxResponse,
11+
StorageBox,
12+
StorageBoxAccessSettings,
13+
StorageBoxFoldersResponse,
14+
StorageBoxSnapshotPlan,
15+
StorageBoxStats,
16+
)
17+
18+
__all__ = [
19+
"BoundStorageBox",
20+
"StorageBoxesClient",
21+
"StorageBoxesPageResult",
22+
"StorageBox",
23+
"StorageBoxSnapshotPlan",
24+
"StorageBoxStats",
25+
"StorageBoxAccessSettings",
26+
"CreateStorageBoxResponse",
27+
"DeleteStorageBoxResponse",
28+
"StorageBoxFoldersResponse",
29+
]

hcloud/storage_boxes/client.py

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING, Any, NamedTuple
4+
5+
from ..actions import BoundAction
6+
from ..core import BoundModelBase, Meta, ResourceClientBase
7+
from ..locations import BoundLocation, Location
8+
from ..storage_box_types import BoundStorageBoxType, StorageBoxType
9+
from .domain import (
10+
CreateStorageBoxResponse,
11+
DeleteStorageBoxResponse,
12+
StorageBox,
13+
StorageBoxAccessSettings,
14+
StorageBoxFoldersResponse,
15+
StorageBoxSnapshotPlan,
16+
StorageBoxStats,
17+
)
18+
19+
if TYPE_CHECKING:
20+
from .._client import Client
21+
22+
23+
class BoundStorageBox(BoundModelBase, StorageBox):
24+
_client: StorageBoxesClient
25+
26+
model = StorageBox
27+
28+
def __init__(
29+
self,
30+
client: StorageBoxesClient,
31+
data: dict[str, Any],
32+
complete: bool = True,
33+
):
34+
raw = data.get("storage_box_type")
35+
if raw is not None:
36+
data["storage_box_type"] = BoundStorageBoxType(
37+
client._parent.storage_box_types, raw
38+
)
39+
40+
raw = data.get("location")
41+
if raw is not None:
42+
data["location"] = BoundLocation(client._parent.locations, raw)
43+
44+
raw = data.get("snapshot_plan")
45+
if raw is not None:
46+
data["snapshot_plan"] = StorageBoxSnapshotPlan.from_dict(raw)
47+
48+
raw = data.get("access_settings")
49+
if raw is not None:
50+
data["access_settings"] = StorageBoxAccessSettings.from_dict(raw)
51+
52+
raw = data.get("stats")
53+
if raw is not None:
54+
data["stats"] = StorageBoxStats.from_dict(raw)
55+
56+
super().__init__(client, data, complete)
57+
58+
# TODO: implement bound methods
59+
60+
61+
class StorageBoxesPageResult(NamedTuple):
62+
storage_boxes: list[BoundStorageBox]
63+
meta: Meta
64+
65+
66+
class StorageBoxesClient(ResourceClientBase):
67+
"""
68+
A client for the Storage Boxes API.
69+
70+
See https://docs.hetzner.cloud/reference/hetzner#storage-boxes.
71+
"""
72+
73+
_base_url = "/storage_boxes"
74+
75+
def __init__(self, client: Client):
76+
super().__init__(client)
77+
self._client = client._client_hetzner
78+
79+
def get_by_id(self, id: int) -> BoundStorageBox:
80+
"""
81+
Returns a specific Storage Box.
82+
83+
See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-get-a-storage-box
84+
85+
:param id: ID of the Storage Box.
86+
"""
87+
response = self._client.request(
88+
method="GET",
89+
url=f"{self._base_url}/{id}",
90+
)
91+
return BoundStorageBox(self, response["storage_box"])
92+
93+
def get_by_name(self, name: str) -> BoundStorageBox | None:
94+
"""
95+
Returns a specific Storage Box.
96+
97+
See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-list-storage-boxes
98+
99+
:param name: Name of the Storage Box.
100+
"""
101+
return self._get_first_by(name=name)
102+
103+
def get_list(
104+
self,
105+
name: str | None = None,
106+
label_selector: str | None = None,
107+
page: int | None = None,
108+
per_page: int | None = None,
109+
) -> StorageBoxesPageResult:
110+
"""
111+
Returns a list of Storage Boxes for a specific page.
112+
113+
See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-list-storage-boxes
114+
115+
:param name: Name of the Storage Box.
116+
:param label_selector: Filter resources by labels. The response will only contain resources matching the label selector.
117+
:param page: Page number to return.
118+
:param per_page: Maximum number of entries returned per page.
119+
"""
120+
params: dict[str, Any] = {}
121+
if name is not None:
122+
params["name"] = name
123+
if label_selector is not None:
124+
params["label_selector"] = label_selector
125+
if page is not None:
126+
params["page"] = page
127+
if per_page is not None:
128+
params["per_page"] = per_page
129+
130+
response = self._client.request(
131+
method="GET",
132+
url=f"{self._base_url}",
133+
params=params,
134+
)
135+
return StorageBoxesPageResult(
136+
storage_boxes=[BoundStorageBox(self, o) for o in response["storage_boxes"]],
137+
meta=Meta.parse_meta(response),
138+
)
139+
140+
def get_all(
141+
self,
142+
name: str | None = None,
143+
label_selector: str | None = None,
144+
) -> list[BoundStorageBox]:
145+
"""
146+
Returns all Storage Boxes.
147+
148+
See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-list-storage-boxes
149+
150+
:param name: Name of the Storage Box.
151+
:param label_selector: Filter resources by labels. The response will only contain resources matching the label selector.
152+
"""
153+
return self._iter_pages(
154+
self.get_list,
155+
name=name,
156+
label_selector=label_selector,
157+
)
158+
159+
def create(
160+
self,
161+
*,
162+
name: str,
163+
password: str,
164+
location: BoundLocation | Location,
165+
storage_box_type: BoundStorageBoxType | StorageBoxType,
166+
ssh_keys: list[str] | None = None,
167+
access_settings: StorageBoxAccessSettings | None = None,
168+
labels: dict[str, str] | None = None,
169+
) -> CreateStorageBoxResponse:
170+
"""
171+
Creates a Storage Box.
172+
173+
See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-create-a-storage-box
174+
175+
:param name: Name of the Storage Box.
176+
:param password: Password of the Storage Box.
177+
:param location: Location of the Storage Box.
178+
:param storage_box_type: Type of the Storage Box.
179+
:param ssh_keys: SSH public keys of the Storage Box.
180+
:param access_settings: Access settings of the Storage Box.
181+
:param labels: User-defined labels (key/value pairs) for the Storage Box.
182+
"""
183+
data: dict[str, Any] = {
184+
"name": name,
185+
"password": password,
186+
"location": location.id_or_name,
187+
"storage_box_type": storage_box_type.id_or_name,
188+
}
189+
if ssh_keys is not None:
190+
data["ssh_keys"] = ssh_keys
191+
if access_settings is not None:
192+
data["access_settings"] = access_settings.to_payload()
193+
if labels is not None:
194+
data["labels"] = labels
195+
196+
response = self._client.request(
197+
method="POST",
198+
url="/storage_boxes",
199+
json=data,
200+
)
201+
202+
return CreateStorageBoxResponse(
203+
storage_box=BoundStorageBox(self, response["storage_box"]),
204+
action=BoundAction(self._parent.actions, response["action"]),
205+
)
206+
207+
def update(
208+
self,
209+
storage_box: BoundStorageBox | StorageBox,
210+
*,
211+
name: str | None = None,
212+
labels: dict[str, str] | None = None,
213+
) -> BoundStorageBox:
214+
"""
215+
Updates a Storage Box.
216+
217+
See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-update-a-storage-box
218+
219+
:param storage_box: Storage Box to update.
220+
:param name: Name of the Storage Box.
221+
:param labels: User-defined labels (key/value pairs) for the Storage Box.
222+
"""
223+
data: dict[str, Any] = {}
224+
if name is not None:
225+
data["name"] = name
226+
if labels is not None:
227+
data["labels"] = labels
228+
229+
response = self._client.request(
230+
method="PUT",
231+
url=f"{self._base_url}/{storage_box.id}",
232+
json=data,
233+
)
234+
235+
return BoundStorageBox(self, response["storage_box"])
236+
237+
def delete(
238+
self,
239+
storage_box: BoundStorageBox | StorageBox,
240+
) -> DeleteStorageBoxResponse:
241+
"""
242+
Deletes a Storage Box.
243+
244+
See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-delete-storage-box
245+
246+
:param storage_box: Storage Box to delete.
247+
"""
248+
response = self._client.request(
249+
method="DELETE",
250+
url=f"{self._base_url}/{storage_box.id}",
251+
)
252+
253+
return DeleteStorageBoxResponse(
254+
action=BoundAction(self._parent.actions, response["action"])
255+
)
256+
257+
def get_folders(
258+
self,
259+
storage_box: BoundStorageBox | StorageBox,
260+
path: str | None = None,
261+
) -> StorageBoxFoldersResponse:
262+
"""
263+
Lists the (sub)folders contained in a Storage Box.
264+
265+
Files are not part of the response.
266+
267+
See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-list-content-of-storage-box
268+
269+
:param storage_box: Storage Box to list the folders from.
270+
:param path: Relative path to list the folders from.
271+
"""
272+
params: dict[str, Any] = {}
273+
if path is not None:
274+
params["path"] = path
275+
276+
response = self._client.request(
277+
method="GET",
278+
url=f"{self._base_url}/{storage_box.id}/folders",
279+
params=params,
280+
)
281+
282+
return StorageBoxFoldersResponse(folders=response["folders"])

0 commit comments

Comments
 (0)