Skip to content

File tree

9 files changed

+991
-0
lines changed

9 files changed

+991
-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

3132

@@ -273,6 +274,12 @@ def __init__(
273274
:type: :class:`StorageBoxTypesClient <hcloud.storage_box_types.client.StorageBoxTypesClient>`
274275
"""
275276

277+
self.storage_boxes = StorageBoxesClient(self)
278+
"""StorageBoxesClient Instance
279+
280+
:type: :class:`StorageBoxesClient <hcloud.storage_boxes.client.StorageBoxesClient>`
281+
"""
282+
276283
def request( # type: ignore[no-untyped-def]
277284
self,
278285
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: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
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+
def __init__(self, client: Client):
74+
super().__init__(client)
75+
self._client = client._client_hetzner
76+
77+
def get_by_id(self, id: int) -> BoundStorageBox:
78+
"""
79+
Returns a specific Storage Box.
80+
81+
See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-get-a-storage-box
82+
83+
:param id: ID of the Storage Box.
84+
"""
85+
response = self._client.request(
86+
method="GET",
87+
url=f"/storage_boxes/{id}",
88+
)
89+
return BoundStorageBox(self, response["storage_box"])
90+
91+
def get_by_name(self, name: str) -> BoundStorageBox | None:
92+
"""
93+
Returns a specific Storage Box.
94+
95+
See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-list-storage-boxes
96+
97+
:param name: Name of the Storage Box.
98+
"""
99+
return self._get_first_by(name=name)
100+
101+
def get_list(
102+
self,
103+
name: str | None = None,
104+
label_selector: str | None = None,
105+
page: int | None = None,
106+
per_page: int | None = None,
107+
) -> StorageBoxesPageResult:
108+
"""
109+
Returns a list of Storage Boxes for a specific page.
110+
111+
See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-list-storage-boxes
112+
113+
:param name: Name of the Storage Box.
114+
:param label_selector: Filter resources by labels. The response will only contain resources matching the label selector.
115+
:param page: Page number to return.
116+
:param per_page: Maximum number of entries returned per page.
117+
"""
118+
params: dict[str, Any] = {}
119+
if name is not None:
120+
params["name"] = name
121+
if label_selector is not None:
122+
params["label_selector"] = label_selector
123+
if page is not None:
124+
params["page"] = page
125+
if per_page is not None:
126+
params["per_page"] = per_page
127+
128+
response = self._client.request(
129+
method="GET",
130+
url="/storage_boxes",
131+
params=params,
132+
)
133+
return StorageBoxesPageResult(
134+
storage_boxes=[BoundStorageBox(self, o) for o in response["storage_boxes"]],
135+
meta=Meta.parse_meta(response),
136+
)
137+
138+
def get_all(
139+
self,
140+
name: str | None = None,
141+
label_selector: str | None = None,
142+
) -> list[BoundStorageBox]:
143+
"""
144+
Returns all Storage Boxes.
145+
146+
See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-list-storage-boxes
147+
148+
:param name: Name of the Storage Box.
149+
:param label_selector: Filter resources by labels. The response will only contain resources matching the label selector.
150+
"""
151+
return self._iter_pages(
152+
self.get_list,
153+
name=name,
154+
label_selector=label_selector,
155+
)
156+
157+
def create(
158+
self,
159+
*,
160+
name: str,
161+
password: str,
162+
location: BoundLocation | Location,
163+
storage_box_type: BoundStorageBoxType | StorageBoxType,
164+
ssh_keys: list[str] | None = None,
165+
access_settings: StorageBoxAccessSettings | None = None,
166+
labels: dict[str, str] | None = None,
167+
) -> CreateStorageBoxResponse:
168+
"""
169+
Creates a Storage Box.
170+
171+
See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-create-a-storage-box
172+
173+
:param name: Name of the Storage Box.
174+
:param password: Password of the Storage Box.
175+
:param location: Location of the Storage Box.
176+
:param storage_box_type: Type of the Storage Box.
177+
:param ssh_keys: SSH public keys of the Storage Box.
178+
:param access_settings: Access settings of the Storage Box.
179+
:param labels: User-defined labels (key/value pairs) for the Storage Box.
180+
"""
181+
data: dict[str, Any] = {
182+
"name": name,
183+
"password": password,
184+
"location": location.name, # TODO: ID or name ?
185+
"storage_box_type": storage_box_type.id_or_name,
186+
}
187+
if ssh_keys is not None:
188+
data["ssh_keys"] = ssh_keys
189+
if access_settings is not None:
190+
data["access_settings"] = access_settings.to_payload()
191+
if labels is not None:
192+
data["labels"] = labels
193+
194+
response = self._client.request(
195+
method="POST",
196+
url="/storage_boxes",
197+
json=data,
198+
)
199+
200+
return CreateStorageBoxResponse(
201+
storage_box=BoundStorageBox(self, response["storage_box"]),
202+
action=BoundAction(self._parent.actions, response["action"]),
203+
)
204+
205+
def update(
206+
self,
207+
storage_box: BoundStorageBox | StorageBox,
208+
*,
209+
name: str | None = None,
210+
labels: dict[str, str] | None = None,
211+
) -> BoundStorageBox:
212+
"""
213+
Updates a Storage Box.
214+
215+
See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-update-a-storage-box
216+
217+
:param storage_box: Storage Box to update.
218+
:param name: Name of the Storage Box.
219+
:param labels: User-defined labels (key/value pairs) for the Storage Box.
220+
"""
221+
data: dict[str, Any] = {}
222+
if name is not None:
223+
data["name"] = name
224+
if labels is not None:
225+
data["labels"] = labels
226+
227+
response = self._client.request(
228+
method="PUT",
229+
url=f"/storage_boxes/{storage_box.id}",
230+
json=data,
231+
)
232+
233+
return BoundStorageBox(self, response["storage_box"])
234+
235+
def delete(
236+
self,
237+
storage_box: BoundStorageBox | StorageBox,
238+
) -> DeleteStorageBoxResponse:
239+
"""
240+
Deletes a Storage Box.
241+
242+
See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-delete-storage-box
243+
244+
:param storage_box: Storage Box to delete.
245+
"""
246+
response = self._client.request(
247+
method="DELETE",
248+
url=f"/storage_boxes/{storage_box.id}",
249+
)
250+
251+
return DeleteStorageBoxResponse(
252+
action=BoundAction(self._parent.actions, response["action"])
253+
)
254+
255+
def get_folders(
256+
self,
257+
storage_box: BoundStorageBox | StorageBox,
258+
path: str | None = None,
259+
) -> StorageBoxFoldersResponse:
260+
"""
261+
Lists the (sub)folders contained in a Storage Box.
262+
263+
Files are not part of the response.
264+
265+
See https://docs.hetzner.cloud/reference/hetzner#storage-boxes-list-content-of-storage-box
266+
267+
:param storage_box: Storage Box to list the folders from.
268+
:param path: Relative path to list the folders from.
269+
"""
270+
params: dict[str, Any] = {}
271+
if path is not None:
272+
params["path"] = path
273+
274+
response = self._client.request(
275+
method="GET",
276+
url=f"/storage_boxes/{storage_box.id}/folders",
277+
params=params,
278+
)
279+
280+
return StorageBoxFoldersResponse(folders=response["folders"])

0 commit comments

Comments
 (0)