Skip to content

Commit 8182777

Browse files
feat(cluster-types): add OpenAPI cluster types service
Co-authored-by: imagene-shahar <imagene-shahar@users.noreply.github.com>
1 parent e524762 commit 8182777

8 files changed

Lines changed: 131 additions & 0 deletions

File tree

datacrunch_compat/datacrunch/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
__version__,
66
authentication,
77
balance,
8+
cluster_types,
89
constants,
910
container_types,
1011
containers,
@@ -29,6 +30,7 @@
2930
'__version__',
3031
'authentication',
3132
'balance',
33+
'cluster_types',
3234
'constants',
3335
'container_types',
3436
'containers',

datacrunch_compat/datacrunch/datacrunch.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from verda._version import __version__
55
from verda.authentication import AuthenticationService
66
from verda.balance import BalanceService
7+
from verda.cluster_types import ClusterTypesService
78
from verda.constants import Constants
89
from verda.container_types import ContainerTypesService
910
from verda.containers import ContainersService
@@ -21,6 +22,7 @@
2122
__all__ = [
2223
'AuthenticationService',
2324
'BalanceService',
25+
'ClusterTypesService',
2426
'Constants',
2527
'ContainerTypesService',
2628
'ContainersService',
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Cluster Types
2+
=============
3+
4+
.. autoclass:: verda.cluster_types.ClusterTypesService
5+
:members:
6+
7+
.. autoclass:: verda.cluster_types.ClusterType
8+
:members:
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import responses # https://github.com/getsentry/responses
2+
3+
from verda.cluster_types import ClusterType, ClusterTypesService
4+
5+
CLUSTER_TYPE_ID = 'cluster-c0de-a5d2-4972-ae4e-d429115d055b'
6+
7+
8+
@responses.activate
9+
def test_cluster_types(http_client):
10+
endpoint = http_client._base_url + '/cluster-types?currency=usd'
11+
responses.add(
12+
responses.GET,
13+
endpoint,
14+
json=[
15+
{
16+
'id': CLUSTER_TYPE_ID,
17+
'model': 'H200',
18+
'name': 'H200 Cluster',
19+
'cluster_type': '16H200',
20+
'cpu': {'description': '64 CPU', 'number_of_cores': 64},
21+
'gpu': {'description': '16x H200', 'number_of_gpus': 16},
22+
'gpu_memory': {'description': '2.2TB VRAM', 'size_in_gigabytes': 2200},
23+
'memory': {'description': '4TB RAM', 'size_in_gigabytes': 4096},
24+
'price_per_hour': '45.50',
25+
'currency': 'usd',
26+
'manufacturer': 'NVIDIA',
27+
'node_details': ['2x 8 GPU nodes'],
28+
'supported_os': ['ubuntu-24.04-cuda-12.8-cluster'],
29+
}
30+
],
31+
status=200,
32+
)
33+
34+
service = ClusterTypesService(http_client)
35+
36+
cluster_types = service.get()
37+
cluster_type = cluster_types[0]
38+
39+
assert isinstance(cluster_types, list)
40+
assert len(cluster_types) == 1
41+
assert isinstance(cluster_type, ClusterType)
42+
assert cluster_type.id == CLUSTER_TYPE_ID
43+
assert cluster_type.model == 'H200'
44+
assert cluster_type.name == 'H200 Cluster'
45+
assert cluster_type.cluster_type == '16H200'
46+
assert cluster_type.price_per_hour == 45.5
47+
assert cluster_type.currency == 'usd'
48+
assert cluster_type.manufacturer == 'NVIDIA'
49+
assert cluster_type.node_details == ['2x 8 GPU nodes']
50+
assert cluster_type.supported_os == ['ubuntu-24.04-cuda-12.8-cluster']
51+
assert responses.assert_call_count(endpoint, 1) is True

tests/unit_tests/test_client.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def test_client(self):
2727
# assert
2828
assert client.constants.base_url == BASE_URL
2929
assert hasattr(client, 'container_types')
30+
assert hasattr(client, 'cluster_types')
3031

3132
@responses.activate
3233
def test_client_with_default_base_url(self):

verda/_verda.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from verda._version import __version__
22
from verda.authentication import AuthenticationService
33
from verda.balance import BalanceService
4+
from verda.cluster_types import ClusterTypesService
45
from verda.clusters import ClustersService
56
from verda.container_types import ContainerTypesService
67
from verda.constants import Constants
@@ -87,5 +88,8 @@ def __init__(
8788
self.clusters: ClustersService = ClustersService(self._http_client)
8889
"""Clusters service. Create and manage compute clusters"""
8990

91+
self.cluster_types: ClusterTypesService = ClusterTypesService(self._http_client)
92+
"""Cluster types service. Get available cluster SKUs"""
93+
9094

9195
__all__ = ['VerdaClient']

verda/cluster_types/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from verda.cluster_types._cluster_types import ClusterType, ClusterTypesService
2+
3+
__all__ = ['ClusterType', 'ClusterTypesService']
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from dataclasses import dataclass
2+
from typing import Literal
3+
4+
from dataclasses_json import dataclass_json
5+
6+
CLUSTER_TYPES_ENDPOINT = '/cluster-types'
7+
8+
Currency = Literal['usd', 'eur']
9+
10+
11+
@dataclass_json
12+
@dataclass
13+
class ClusterType:
14+
"""Cluster type returned by the public API."""
15+
16+
id: str
17+
model: str
18+
name: str
19+
cluster_type: str
20+
cpu: dict
21+
gpu: dict
22+
gpu_memory: dict
23+
memory: dict
24+
price_per_hour: float
25+
currency: Currency
26+
manufacturer: str
27+
node_details: list[str]
28+
supported_os: list[str]
29+
30+
31+
class ClusterTypesService:
32+
"""Service for interacting with cluster types."""
33+
34+
def __init__(self, http_client) -> None:
35+
self._http_client = http_client
36+
37+
def get(self, currency: Currency = 'usd') -> list[ClusterType]:
38+
"""Return all available cluster types."""
39+
cluster_types = self._http_client.get(
40+
CLUSTER_TYPES_ENDPOINT,
41+
params={'currency': currency},
42+
).json()
43+
return [
44+
ClusterType(
45+
id=cluster_type['id'],
46+
model=cluster_type['model'],
47+
name=cluster_type['name'],
48+
cluster_type=cluster_type['cluster_type'],
49+
cpu=cluster_type['cpu'],
50+
gpu=cluster_type['gpu'],
51+
gpu_memory=cluster_type['gpu_memory'],
52+
memory=cluster_type['memory'],
53+
price_per_hour=float(cluster_type['price_per_hour']),
54+
currency=cluster_type['currency'],
55+
manufacturer=cluster_type['manufacturer'],
56+
node_details=cluster_type['node_details'],
57+
supported_os=cluster_type['supported_os'],
58+
)
59+
for cluster_type in cluster_types
60+
]

0 commit comments

Comments
 (0)