Skip to content

Commit 250b5d4

Browse files
feat(cloud): add delete_and_poll and examples for GPU baremetal clusters
- Add missing delete_and_poll polling method for baremetal clusters (sync + async + RawResponse/Streaming wrappers) - Add runnable examples: gpu_baremetal_clusters.py and gpu_baremetal_clusters_async.py
1 parent 2240449 commit 250b5d4

3 files changed

Lines changed: 487 additions & 0 deletions

File tree

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
from typing import List
2+
3+
from gcore import Gcore
4+
from gcore.types.cloud.gpu_image import GPUImage
5+
from gcore.types.cloud.network_interface import NetworkInterface
6+
from gcore.types.cloud.gpu_baremetal.cluster_create_params import (
7+
ServersSettings,
8+
ServersSettingsInterfaceExternalInterfaceInputSerializer,
9+
)
10+
from gcore.types.cloud.gpu_baremetal.gpu_baremetal_cluster import GPUBaremetalCluster
11+
from gcore.types.cloud.gpu_baremetal.clusters.gpu_baremetal_flavor import GPUBaremetalFlavor
12+
13+
14+
def main() -> None:
15+
# TODO set API key before running
16+
# api_key = os.environ["GCORE_API_KEY"]
17+
# TODO set cloud project ID before running
18+
# cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"]
19+
# TODO set cloud region ID before running
20+
# cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"]
21+
22+
gcore = Gcore(
23+
# No need to explicitly pass to Gcore constructor if using environment variables
24+
# api_key=api_key,
25+
# cloud_project_id=cloud_project_id,
26+
# cloud_region_id=cloud_region_id,
27+
)
28+
29+
# Flavors
30+
flavors = list_flavors(client=gcore)
31+
32+
# Images
33+
images = list_images(client=gcore)
34+
35+
# Clusters
36+
flavor_name = _get_first_available_flavor(flavors)
37+
image_id = _get_first_image(images)
38+
cluster = create_cluster(client=gcore, flavor=flavor_name, image_id=image_id)
39+
get_cluster(client=gcore, cluster_id=cluster.id)
40+
list_clusters(client=gcore)
41+
update_cluster(client=gcore, cluster_id=cluster.id)
42+
43+
# Interfaces
44+
list_interfaces(client=gcore, cluster_id=cluster.id)
45+
46+
# Servers
47+
list_servers(client=gcore, cluster_id=cluster.id)
48+
49+
# Delete
50+
delete_cluster(client=gcore, cluster_id=cluster.id)
51+
52+
53+
def create_cluster(*, client: Gcore, flavor: str, image_id: str) -> GPUBaremetalCluster:
54+
print("\n=== CREATE GPU BAREMETAL CLUSTER ===")
55+
cluster = client.cloud.gpu_baremetal.clusters.create_and_poll(
56+
name="gcore-python-example-gpu-baremetal",
57+
flavor=flavor,
58+
image_id=image_id,
59+
servers_count=1,
60+
servers_settings=ServersSettings(
61+
interfaces=[
62+
ServersSettingsInterfaceExternalInterfaceInputSerializer(type="external"),
63+
],
64+
),
65+
tags={"name": "gcore-python-example"},
66+
)
67+
print(f"Created cluster: ID={cluster.id}, name={cluster.name}, status={cluster.status}")
68+
print("========================")
69+
return cluster
70+
71+
72+
def get_cluster(*, client: Gcore, cluster_id: str) -> GPUBaremetalCluster:
73+
print("\n=== GET GPU BAREMETAL CLUSTER ===")
74+
cluster = client.cloud.gpu_baremetal.clusters.get(cluster_id=cluster_id)
75+
print(f"Cluster: ID={cluster.id}, name={cluster.name}, status={cluster.status}")
76+
print("========================")
77+
return cluster
78+
79+
80+
def list_clusters(*, client: Gcore) -> None:
81+
print("\n=== LIST GPU BAREMETAL CLUSTERS ===")
82+
clusters = client.cloud.gpu_baremetal.clusters.list()
83+
for count, cluster in enumerate(clusters, 1):
84+
print(f" {count}. Cluster: ID={cluster.id}, name={cluster.name}, status={cluster.status}")
85+
print("========================")
86+
87+
88+
def update_cluster(*, client: Gcore, cluster_id: str) -> None:
89+
print("\n=== UPDATE GPU BAREMETAL CLUSTER ===")
90+
cluster = client.cloud.gpu_baremetal.clusters.update(
91+
cluster_id=cluster_id, name="gcore-python-example-gpu-baremetal-updated"
92+
)
93+
print(f"Updated cluster: ID={cluster.id}, name={cluster.name}")
94+
print("========================")
95+
96+
97+
def delete_cluster(*, client: Gcore, cluster_id: str) -> None:
98+
print("\n=== DELETE GPU BAREMETAL CLUSTER ===")
99+
client.cloud.gpu_baremetal.clusters.delete_and_poll(
100+
cluster_id=cluster_id,
101+
all_floating_ips=True,
102+
)
103+
print(f"Deleted cluster: ID={cluster_id}")
104+
print("========================")
105+
106+
107+
def list_interfaces(*, client: Gcore, cluster_id: str) -> List[NetworkInterface]:
108+
print("\n=== LIST GPU BAREMETAL CLUSTER INTERFACES ===")
109+
interfaces = client.cloud.gpu_baremetal.clusters.interfaces.list(cluster_id=cluster_id)
110+
for count, interface in enumerate(interfaces.results, 1):
111+
print(f" {count}. Interface: PortID={interface.port_id}, NetworkID={interface.network_id}")
112+
print("========================")
113+
return interfaces.results
114+
115+
116+
def list_servers(*, client: Gcore, cluster_id: str) -> None:
117+
print("\n=== LIST GPU BAREMETAL CLUSTER SERVERS ===")
118+
servers = client.cloud.gpu_baremetal.clusters.servers.list(cluster_id=cluster_id)
119+
for count, server in enumerate(servers.results, 1):
120+
print(f" {count}. Server: ID={server.id}, name={server.name}, status={server.status}")
121+
print("========================")
122+
123+
124+
def list_flavors(*, client: Gcore) -> List[GPUBaremetalFlavor]:
125+
print("\n=== LIST GPU BAREMETAL FLAVORS ===")
126+
flavors = client.cloud.gpu_baremetal.clusters.flavors.list()
127+
_print_flavor_details(flavors.results)
128+
print("========================")
129+
return flavors.results
130+
131+
132+
def list_images(*, client: Gcore) -> List[GPUImage]:
133+
print("\n=== LIST GPU BAREMETAL IMAGES ===")
134+
images = client.cloud.gpu_baremetal.clusters.images.list()
135+
_print_image_details(images.results)
136+
print("========================")
137+
return images.results
138+
139+
140+
def _print_flavor_details(flavors: List[GPUBaremetalFlavor]) -> None:
141+
display_count = 3
142+
if len(flavors) < display_count:
143+
display_count = len(flavors)
144+
145+
for i in range(display_count):
146+
flavor = flavors[i]
147+
print(f" {i + 1}. Flavor: name={flavor.name}")
148+
print(f" Capacity: {flavor.capacity}")
149+
status = "AVAILABLE"
150+
if flavor.disabled:
151+
status = "DISABLED"
152+
print(f" Status: {status}")
153+
print()
154+
155+
if len(flavors) > display_count:
156+
print(f" ... and {len(flavors) - display_count} more flavors")
157+
158+
159+
def _print_image_details(images: List[GPUImage]) -> None:
160+
display_count = 3
161+
if len(images) < display_count:
162+
display_count = len(images)
163+
164+
for i in range(display_count):
165+
img = images[i]
166+
print(f" {i + 1}. Image ID: {img.id}, name: {img.name}, OS type: {img.os_type}, status: {img.status}")
167+
168+
if len(images) > display_count:
169+
print(f" ... and {len(images) - display_count} more images")
170+
171+
172+
def _get_first_available_flavor(flavors: List[GPUBaremetalFlavor]) -> str:
173+
available_flavors = [f for f in flavors if not f.disabled and f.capacity > 0]
174+
if not available_flavors:
175+
raise ValueError("No available flavors with capacity found")
176+
selected = available_flavors[0]
177+
print(f"Selected flavor: {selected.name} (Capacity: {selected.capacity})")
178+
return selected.name
179+
180+
181+
def _get_first_image(images: List[GPUImage]) -> str:
182+
if not images:
183+
raise ValueError("No images found")
184+
selected = images[0]
185+
print(f"Selected image: {selected.name} (ID: {selected.id})")
186+
return selected.id
187+
188+
189+
if __name__ == "__main__":
190+
main()
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import asyncio
2+
from typing import List
3+
4+
from gcore import AsyncGcore
5+
from gcore.types.cloud.gpu_image import GPUImage
6+
from gcore.types.cloud.network_interface import NetworkInterface
7+
from gcore.types.cloud.gpu_baremetal.cluster_create_params import (
8+
ServersSettings,
9+
ServersSettingsInterfaceExternalInterfaceInputSerializer,
10+
)
11+
from gcore.types.cloud.gpu_baremetal.gpu_baremetal_cluster import GPUBaremetalCluster
12+
from gcore.types.cloud.gpu_baremetal.clusters.gpu_baremetal_flavor import GPUBaremetalFlavor
13+
14+
15+
async def main() -> None:
16+
# TODO set API key before running
17+
# api_key = os.environ["GCORE_API_KEY"]
18+
# TODO set cloud project ID before running
19+
# cloud_project_id = os.environ["GCORE_CLOUD_PROJECT_ID"]
20+
# TODO set cloud region ID before running
21+
# cloud_region_id = os.environ["GCORE_CLOUD_REGION_ID"]
22+
23+
gcore = AsyncGcore(
24+
# No need to explicitly pass to AsyncGcore constructor if using environment variables
25+
# api_key=api_key,
26+
# cloud_project_id=cloud_project_id,
27+
# cloud_region_id=cloud_region_id,
28+
)
29+
30+
# Flavors
31+
flavors = await list_flavors(client=gcore)
32+
33+
# Images
34+
images = await list_images(client=gcore)
35+
36+
# Clusters
37+
flavor_name = _get_first_available_flavor(flavors)
38+
image_id = _get_first_image(images)
39+
cluster = await create_cluster(client=gcore, flavor=flavor_name, image_id=image_id)
40+
await get_cluster(client=gcore, cluster_id=cluster.id)
41+
await list_clusters(client=gcore)
42+
await update_cluster(client=gcore, cluster_id=cluster.id)
43+
44+
# Interfaces
45+
await list_interfaces(client=gcore, cluster_id=cluster.id)
46+
47+
# Servers
48+
await list_servers(client=gcore, cluster_id=cluster.id)
49+
50+
# Delete
51+
await delete_cluster(client=gcore, cluster_id=cluster.id)
52+
53+
54+
async def create_cluster(*, client: AsyncGcore, flavor: str, image_id: str) -> GPUBaremetalCluster:
55+
print("\n=== CREATE GPU BAREMETAL CLUSTER ===")
56+
cluster = await client.cloud.gpu_baremetal.clusters.create_and_poll(
57+
name="gcore-python-example-gpu-baremetal",
58+
flavor=flavor,
59+
image_id=image_id,
60+
servers_count=1,
61+
servers_settings=ServersSettings(
62+
interfaces=[
63+
ServersSettingsInterfaceExternalInterfaceInputSerializer(type="external"),
64+
],
65+
),
66+
tags={"name": "gcore-python-example"},
67+
)
68+
print(f"Created cluster: ID={cluster.id}, name={cluster.name}, status={cluster.status}")
69+
print("========================")
70+
return cluster
71+
72+
73+
async def get_cluster(*, client: AsyncGcore, cluster_id: str) -> GPUBaremetalCluster:
74+
print("\n=== GET GPU BAREMETAL CLUSTER ===")
75+
cluster = await client.cloud.gpu_baremetal.clusters.get(cluster_id=cluster_id)
76+
print(f"Cluster: ID={cluster.id}, name={cluster.name}, status={cluster.status}")
77+
print("========================")
78+
return cluster
79+
80+
81+
async def list_clusters(*, client: AsyncGcore) -> None:
82+
print("\n=== LIST GPU BAREMETAL CLUSTERS ===")
83+
clusters = await client.cloud.gpu_baremetal.clusters.list()
84+
count = 0
85+
async for cluster in clusters:
86+
count += 1
87+
print(f" {count}. Cluster: ID={cluster.id}, name={cluster.name}, status={cluster.status}")
88+
print("========================")
89+
90+
91+
async def update_cluster(*, client: AsyncGcore, cluster_id: str) -> None:
92+
print("\n=== UPDATE GPU BAREMETAL CLUSTER ===")
93+
cluster = await client.cloud.gpu_baremetal.clusters.update(
94+
cluster_id=cluster_id, name="gcore-python-example-gpu-baremetal-updated"
95+
)
96+
print(f"Updated cluster: ID={cluster.id}, name={cluster.name}")
97+
print("========================")
98+
99+
100+
async def delete_cluster(*, client: AsyncGcore, cluster_id: str) -> None:
101+
print("\n=== DELETE GPU BAREMETAL CLUSTER ===")
102+
await client.cloud.gpu_baremetal.clusters.delete_and_poll(
103+
cluster_id=cluster_id,
104+
all_floating_ips=True,
105+
)
106+
print(f"Deleted cluster: ID={cluster_id}")
107+
print("========================")
108+
109+
110+
async def list_interfaces(*, client: AsyncGcore, cluster_id: str) -> List[NetworkInterface]:
111+
print("\n=== LIST GPU BAREMETAL CLUSTER INTERFACES ===")
112+
interfaces = await client.cloud.gpu_baremetal.clusters.interfaces.list(cluster_id=cluster_id)
113+
for count, interface in enumerate(interfaces.results, 1):
114+
print(f" {count}. Interface: PortID={interface.port_id}, NetworkID={interface.network_id}")
115+
print("========================")
116+
return interfaces.results
117+
118+
119+
async def list_servers(*, client: AsyncGcore, cluster_id: str) -> None:
120+
print("\n=== LIST GPU BAREMETAL CLUSTER SERVERS ===")
121+
servers = await client.cloud.gpu_baremetal.clusters.servers.list(cluster_id=cluster_id)
122+
for count, server in enumerate(servers.results, 1):
123+
print(f" {count}. Server: ID={server.id}, name={server.name}, status={server.status}")
124+
print("========================")
125+
126+
127+
async def list_flavors(*, client: AsyncGcore) -> List[GPUBaremetalFlavor]:
128+
print("\n=== LIST GPU BAREMETAL FLAVORS ===")
129+
flavors = await client.cloud.gpu_baremetal.clusters.flavors.list()
130+
_print_flavor_details(flavors.results)
131+
print("========================")
132+
return flavors.results
133+
134+
135+
async def list_images(*, client: AsyncGcore) -> List[GPUImage]:
136+
print("\n=== LIST GPU BAREMETAL IMAGES ===")
137+
images = await client.cloud.gpu_baremetal.clusters.images.list()
138+
_print_image_details(images.results)
139+
print("========================")
140+
return images.results
141+
142+
143+
def _print_flavor_details(flavors: List[GPUBaremetalFlavor]) -> None:
144+
display_count = 3
145+
if len(flavors) < display_count:
146+
display_count = len(flavors)
147+
148+
for i in range(display_count):
149+
flavor = flavors[i]
150+
print(f" {i + 1}. Flavor: name={flavor.name}")
151+
print(f" Capacity: {flavor.capacity}")
152+
status = "AVAILABLE"
153+
if flavor.disabled:
154+
status = "DISABLED"
155+
print(f" Status: {status}")
156+
print()
157+
158+
if len(flavors) > display_count:
159+
print(f" ... and {len(flavors) - display_count} more flavors")
160+
161+
162+
def _print_image_details(images: List[GPUImage]) -> None:
163+
display_count = 3
164+
if len(images) < display_count:
165+
display_count = len(images)
166+
167+
for i in range(display_count):
168+
img = images[i]
169+
print(f" {i + 1}. Image ID: {img.id}, name: {img.name}, OS type: {img.os_type}, status: {img.status}")
170+
171+
if len(images) > display_count:
172+
print(f" ... and {len(images) - display_count} more images")
173+
174+
175+
def _get_first_available_flavor(flavors: List[GPUBaremetalFlavor]) -> str:
176+
available_flavors = [f for f in flavors if not f.disabled and f.capacity > 0]
177+
if not available_flavors:
178+
raise ValueError("No available flavors with capacity found")
179+
selected = available_flavors[0]
180+
print(f"Selected flavor: {selected.name} (Capacity: {selected.capacity})")
181+
return selected.name
182+
183+
184+
def _get_first_image(images: List[GPUImage]) -> str:
185+
if not images:
186+
raise ValueError("No images found")
187+
selected = images[0]
188+
print(f"Selected image: {selected.name} (ID: {selected.id})")
189+
return selected.id
190+
191+
192+
if __name__ == "__main__":
193+
asyncio.run(main())

0 commit comments

Comments
 (0)