Skip to content

File tree

9 files changed

+998
-0
lines changed

9 files changed

+998
-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: 8 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

@@ -121,6 +122,7 @@ class Client:
121122
)
122123
_retry_max_retries = 5
123124

125+
# pylint: disable=too-many-statements
124126
def __init__(
125127
self,
126128
token: str,
@@ -257,6 +259,12 @@ def __init__(
257259
:type: :class:`StorageBoxTypesClient <hcloud.storage_box_types.client.StorageBoxTypesClient>`
258260
"""
259261

262+
self.storage_boxes = StorageBoxesClient(self)
263+
"""StorageBoxesClient Instance
264+
265+
:type: :class:`StorageBoxesClient <hcloud.storage_boxes.client.StorageBoxesClient>`
266+
"""
267+
260268
def _get_user_agent(self) -> str:
261269
"""Get the user agent of the hcloud-python instance with the user application name (if specified)
262270

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

0 commit comments

Comments
 (0)