Skip to content

Commit d6b3e1d

Browse files
authored
[SDK] Add hardware API support (#141)
Add create_hardware_instance(request) and delete_hardware_instance(hardware_instance_id) to CentMLClient
1 parent bf1fb4f commit d6b3e1d

3 files changed

Lines changed: 123 additions & 1 deletion

File tree

centml/sdk/api.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
CreateComputeDeploymentRequest,
99
CreateCServeV3DeploymentRequest,
1010
CreateJobDeploymentRequest,
11+
CreateHardwareInstanceRequest,
1112
ApiException,
1213
InviteUserRequest,
1314
Metric,
@@ -121,6 +122,12 @@ def get_hardware_instances(self, cluster_id=None):
121122
cluster_id=cluster_id if cluster_id else None
122123
).results
123124

125+
def create_hardware_instance(self, request: CreateHardwareInstanceRequest):
126+
return self._api.create_hardware_instance_hardware_instances_post(request)
127+
128+
def delete_hardware_instance(self, hardware_instance_id: int):
129+
return self._api.delete_hardware_instance_hardware_instances_hardware_instance_id_delete(hardware_instance_id)
130+
124131
def get_prebuilt_images(self, depl_type: DeploymentType):
125132
return self._api.get_prebuilt_images_prebuilt_images_get(type=depl_type)
126133

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Example showing how to manage hardware instances with the CentML SDK.
4+
5+
Covers listing, creating and deleting hardware instances. Running this script
6+
lists the hardware instances you have access to; the create/delete helpers show
7+
the call pattern and are not invoked automatically.
8+
9+
This uses the centml CLI authentication, so make sure you are logged in to the
10+
centml CLI before running it. Creating and deleting hardware instances requires
11+
admin privileges (PERM_ADMIN_MANAGE_HARDWARE) on your CentML organization.
12+
"""
13+
14+
from centml.sdk import CreateHardwareInstanceRequest
15+
from centml.sdk.api import get_centml_client
16+
17+
18+
def list_hardware_instances():
19+
"""List hardware instances, showing the cluster they belong to by name."""
20+
with get_centml_client() as client:
21+
clusters = {c.id: c for c in client.get_clusters().results}
22+
instances = client.get_hardware_instances()
23+
24+
if not instances:
25+
print("No hardware instances found.")
26+
return
27+
28+
print(f"\nFound {len(instances)} hardware instance(s)\n")
29+
for hw in sorted(instances, key=lambda x: x.id):
30+
cluster = clusters.get(hw.cluster_id)
31+
cluster_name = cluster.display_name if cluster else f"cluster {hw.cluster_id}"
32+
print(f"Name: {hw.name}")
33+
print(f"Cluster: {cluster_name}")
34+
print(f"GPU Type: {hw.gpu_type}")
35+
print(f"Num GPUs: {hw.num_gpu}")
36+
print(f"CPU: {hw.cpu}")
37+
print(f"Memory: {hw.memory}")
38+
print("-" * 40)
39+
40+
41+
def create_hardware_instance():
42+
"""Create a hardware instance (requires admin privileges)."""
43+
request = CreateHardwareInstanceRequest(
44+
cluster_id=1,
45+
name="h100-8x",
46+
gpu_type="H100",
47+
num_gpu=8,
48+
cpu=64000,
49+
memory=128000,
50+
accelerator_resource_key="nvidia.com/gpu",
51+
node_affinity_labels={"gpu": "h100"},
52+
accelerator_memory=80000,
53+
)
54+
with get_centml_client() as client:
55+
instance = client.create_hardware_instance(request)
56+
print(f"Created hardware instance '{instance.name}' with ID {instance.id}")
57+
return instance.id
58+
59+
60+
def delete_hardware_instance(hardware_instance_id):
61+
"""Delete a hardware instance by ID (requires admin privileges)."""
62+
with get_centml_client() as client:
63+
client.delete_hardware_instance(hardware_instance_id)
64+
print(f"Deleted hardware instance {hardware_instance_id}")
65+
66+
67+
if __name__ == "__main__":
68+
list_hardware_instances()

tests/test_sdk_api.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from types import SimpleNamespace
22
from unittest.mock import MagicMock
33

4-
from platform_api_python_client import CreateJobDeploymentRequest
4+
from platform_api_python_client import CreateJobDeploymentRequest, CreateHardwareInstanceRequest
55

66
from centml.sdk import ApiException
77
from centml.sdk.api import CentMLClient
@@ -72,3 +72,50 @@ def test_create_job_delegates_to_platform_client():
7272

7373
assert response is expected_response
7474
api.create_job_deployment_deployments_job_post.assert_called_once_with(request)
75+
76+
77+
def test_get_hardware_instances_returns_results():
78+
api = MagicMock()
79+
expected_results = [SimpleNamespace(id=1), SimpleNamespace(id=2)]
80+
api.get_hardware_instances_hardware_instances_get.return_value = SimpleNamespace(results=expected_results)
81+
client = CentMLClient(api)
82+
83+
response = client.get_hardware_instances(cluster_id=5)
84+
85+
assert response is expected_results
86+
api.get_hardware_instances_hardware_instances_get.assert_called_once_with(cluster_id=5)
87+
88+
89+
def test_create_hardware_instance_delegates_to_platform_client():
90+
api = MagicMock()
91+
expected_response = MagicMock()
92+
api.create_hardware_instance_hardware_instances_post.return_value = expected_response
93+
request = CreateHardwareInstanceRequest(
94+
cluster_id=1,
95+
name="h100-test",
96+
gpu_type="H100",
97+
num_gpu=8,
98+
cpu=64000,
99+
memory=128000,
100+
accelerator_resource_key="nvidia.com/gpu",
101+
node_affinity_labels={"gpu": "h100"},
102+
accelerator_memory=80000,
103+
)
104+
client = CentMLClient(api)
105+
106+
response = client.create_hardware_instance(request)
107+
108+
assert response is expected_response
109+
api.create_hardware_instance_hardware_instances_post.assert_called_once_with(request)
110+
111+
112+
def test_delete_hardware_instance_delegates_to_platform_client():
113+
api = MagicMock()
114+
expected_response = MagicMock()
115+
api.delete_hardware_instance_hardware_instances_hardware_instance_id_delete.return_value = expected_response
116+
client = CentMLClient(api)
117+
118+
response = client.delete_hardware_instance(123)
119+
120+
assert response is expected_response
121+
api.delete_hardware_instance_hardware_instances_hardware_instance_id_delete.assert_called_once_with(123)

0 commit comments

Comments
 (0)