Skip to content

Commit 173c67e

Browse files
feat(cloud): add polling methods and examples for GPU virtual clusters
- Add polling methods for GPU virtual cluster resources: - clusters: create_and_poll, delete_and_poll, action_and_poll - images: delete_and_poll, upload_and_poll - servers: delete_and_poll - Add runnable examples: gpu_virtual_clusters.py and gpu_virtual_clusters_async.py
1 parent 250b5d4 commit 173c67e

5 files changed

Lines changed: 1305 additions & 0 deletions

File tree

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
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.gpu_virtual.gpu_virtual_cluster import GPUVirtualCluster
6+
from gcore.types.cloud.gpu_virtual.cluster_create_params import (
7+
ServersSettings,
8+
ServersSettingsVolumeImageVolumeInputSerializer,
9+
ServersSettingsInterfaceExternalInterfaceInputSerializer,
10+
)
11+
from gcore.types.cloud.gpu_virtual.clusters.gpu_virtual_flavor import GPUVirtualFlavor
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+
# Servers
44+
list_servers(client=gcore, cluster_id=cluster.id)
45+
46+
# Delete
47+
delete_cluster(client=gcore, cluster_id=cluster.id)
48+
49+
50+
def create_cluster(*, client: Gcore, flavor: str, image_id: str) -> GPUVirtualCluster:
51+
print("\n=== CREATE GPU VIRTUAL CLUSTER ===")
52+
cluster = client.cloud.gpu_virtual.clusters.create_and_poll(
53+
name="gcore-python-example-gpu-virtual",
54+
flavor=flavor,
55+
servers_count=1,
56+
servers_settings=ServersSettings(
57+
interfaces=[
58+
ServersSettingsInterfaceExternalInterfaceInputSerializer(type="external"),
59+
],
60+
volumes=[
61+
ServersSettingsVolumeImageVolumeInputSerializer(
62+
name="gcore-python-example-volume",
63+
size=50,
64+
type="standard",
65+
source="image",
66+
image_id=image_id,
67+
boot_index=0,
68+
),
69+
],
70+
),
71+
tags={"name": "gcore-python-example"},
72+
)
73+
print(f"Created cluster: ID={cluster.id}, name={cluster.name}, status={cluster.status}")
74+
print("========================")
75+
return cluster
76+
77+
78+
def get_cluster(*, client: Gcore, cluster_id: str) -> GPUVirtualCluster:
79+
print("\n=== GET GPU VIRTUAL CLUSTER ===")
80+
cluster = client.cloud.gpu_virtual.clusters.get(cluster_id=cluster_id)
81+
print(f"Cluster: ID={cluster.id}, name={cluster.name}, status={cluster.status}")
82+
print("========================")
83+
return cluster
84+
85+
86+
def list_clusters(*, client: Gcore) -> None:
87+
print("\n=== LIST GPU VIRTUAL CLUSTERS ===")
88+
clusters = client.cloud.gpu_virtual.clusters.list()
89+
for count, cluster in enumerate(clusters, 1):
90+
print(f" {count}. Cluster: ID={cluster.id}, name={cluster.name}, status={cluster.status}")
91+
print("========================")
92+
93+
94+
def update_cluster(*, client: Gcore, cluster_id: str) -> None:
95+
print("\n=== UPDATE GPU VIRTUAL CLUSTER ===")
96+
cluster = client.cloud.gpu_virtual.clusters.update(
97+
cluster_id=cluster_id, name="gcore-python-example-gpu-virtual-updated"
98+
)
99+
print(f"Updated cluster: ID={cluster.id}, name={cluster.name}")
100+
print("========================")
101+
102+
103+
def delete_cluster(*, client: Gcore, cluster_id: str) -> None:
104+
print("\n=== DELETE GPU VIRTUAL CLUSTER ===")
105+
client.cloud.gpu_virtual.clusters.delete_and_poll(
106+
cluster_id=cluster_id,
107+
all_volumes=True,
108+
all_floating_ips=True,
109+
)
110+
print(f"Deleted cluster: ID={cluster_id}")
111+
print("========================")
112+
113+
114+
def list_servers(*, client: Gcore, cluster_id: str) -> None:
115+
print("\n=== LIST GPU VIRTUAL CLUSTER SERVERS ===")
116+
servers = client.cloud.gpu_virtual.clusters.servers.list(cluster_id=cluster_id)
117+
for count, server in enumerate(servers.results, 1):
118+
print(f" {count}. Server: ID={server.id}, name={server.name}, status={server.status}")
119+
print("========================")
120+
121+
122+
def list_flavors(*, client: Gcore) -> List[GPUVirtualFlavor]:
123+
print("\n=== LIST GPU VIRTUAL FLAVORS ===")
124+
flavors = client.cloud.gpu_virtual.clusters.flavors.list()
125+
_print_flavor_details(flavors.results)
126+
print("========================")
127+
return flavors.results
128+
129+
130+
def list_images(*, client: Gcore) -> List[GPUImage]:
131+
print("\n=== LIST GPU VIRTUAL IMAGES ===")
132+
images = client.cloud.gpu_virtual.clusters.images.list()
133+
_print_image_details(images.results)
134+
print("========================")
135+
return images.results
136+
137+
138+
def _print_flavor_details(flavors: List[GPUVirtualFlavor]) -> None:
139+
display_count = 3
140+
if len(flavors) < display_count:
141+
display_count = len(flavors)
142+
143+
for i in range(display_count):
144+
flavor = flavors[i]
145+
print(f" {i + 1}. Flavor: name={flavor.name}")
146+
print(f" Capacity: {flavor.capacity}")
147+
status = "AVAILABLE"
148+
if flavor.disabled:
149+
status = "DISABLED"
150+
print(f" Status: {status}")
151+
print()
152+
153+
if len(flavors) > display_count:
154+
print(f" ... and {len(flavors) - display_count} more flavors")
155+
156+
157+
def _print_image_details(images: List[GPUImage]) -> None:
158+
display_count = 3
159+
if len(images) < display_count:
160+
display_count = len(images)
161+
162+
for i in range(display_count):
163+
img = images[i]
164+
print(f" {i + 1}. Image ID: {img.id}, name: {img.name}, OS type: {img.os_type}, status: {img.status}")
165+
166+
if len(images) > display_count:
167+
print(f" ... and {len(images) - display_count} more images")
168+
169+
170+
def _get_first_available_flavor(flavors: List[GPUVirtualFlavor]) -> str:
171+
available_flavors = [f for f in flavors if not f.disabled and f.capacity > 0]
172+
if not available_flavors:
173+
raise ValueError("No available flavors with capacity found")
174+
selected = available_flavors[0]
175+
print(f"Selected flavor: {selected.name} (Capacity: {selected.capacity})")
176+
return selected.name
177+
178+
179+
def _get_first_image(images: List[GPUImage]) -> str:
180+
if not images:
181+
raise ValueError("No images found")
182+
selected = images[0]
183+
print(f"Selected image: {selected.name} (ID: {selected.id})")
184+
return selected.id
185+
186+
187+
if __name__ == "__main__":
188+
main()
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
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.gpu_virtual.gpu_virtual_cluster import GPUVirtualCluster
7+
from gcore.types.cloud.gpu_virtual.cluster_create_params import (
8+
ServersSettings,
9+
ServersSettingsVolumeImageVolumeInputSerializer,
10+
ServersSettingsInterfaceExternalInterfaceInputSerializer,
11+
)
12+
from gcore.types.cloud.gpu_virtual.clusters.gpu_virtual_flavor import GPUVirtualFlavor
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+
# Servers
45+
await list_servers(client=gcore, cluster_id=cluster.id)
46+
47+
# Delete
48+
await delete_cluster(client=gcore, cluster_id=cluster.id)
49+
50+
51+
async def create_cluster(*, client: AsyncGcore, flavor: str, image_id: str) -> GPUVirtualCluster:
52+
print("\n=== CREATE GPU VIRTUAL CLUSTER ===")
53+
cluster = await client.cloud.gpu_virtual.clusters.create_and_poll(
54+
name="gcore-python-example-gpu-virtual",
55+
flavor=flavor,
56+
servers_count=1,
57+
servers_settings=ServersSettings(
58+
interfaces=[
59+
ServersSettingsInterfaceExternalInterfaceInputSerializer(type="external"),
60+
],
61+
volumes=[
62+
ServersSettingsVolumeImageVolumeInputSerializer(
63+
name="gcore-python-example-volume",
64+
size=50,
65+
type="standard",
66+
source="image",
67+
image_id=image_id,
68+
boot_index=0,
69+
),
70+
],
71+
),
72+
tags={"name": "gcore-python-example"},
73+
)
74+
print(f"Created cluster: ID={cluster.id}, name={cluster.name}, status={cluster.status}")
75+
print("========================")
76+
return cluster
77+
78+
79+
async def get_cluster(*, client: AsyncGcore, cluster_id: str) -> GPUVirtualCluster:
80+
print("\n=== GET GPU VIRTUAL CLUSTER ===")
81+
cluster = await client.cloud.gpu_virtual.clusters.get(cluster_id=cluster_id)
82+
print(f"Cluster: ID={cluster.id}, name={cluster.name}, status={cluster.status}")
83+
print("========================")
84+
return cluster
85+
86+
87+
async def list_clusters(*, client: AsyncGcore) -> None:
88+
print("\n=== LIST GPU VIRTUAL CLUSTERS ===")
89+
clusters = await client.cloud.gpu_virtual.clusters.list()
90+
count = 0
91+
async for cluster in clusters:
92+
count += 1
93+
print(f" {count}. Cluster: ID={cluster.id}, name={cluster.name}, status={cluster.status}")
94+
print("========================")
95+
96+
97+
async def update_cluster(*, client: AsyncGcore, cluster_id: str) -> None:
98+
print("\n=== UPDATE GPU VIRTUAL CLUSTER ===")
99+
cluster = await client.cloud.gpu_virtual.clusters.update(
100+
cluster_id=cluster_id, name="gcore-python-example-gpu-virtual-updated"
101+
)
102+
print(f"Updated cluster: ID={cluster.id}, name={cluster.name}")
103+
print("========================")
104+
105+
106+
async def delete_cluster(*, client: AsyncGcore, cluster_id: str) -> None:
107+
print("\n=== DELETE GPU VIRTUAL CLUSTER ===")
108+
await client.cloud.gpu_virtual.clusters.delete_and_poll(
109+
cluster_id=cluster_id,
110+
all_volumes=True,
111+
all_floating_ips=True,
112+
)
113+
print(f"Deleted cluster: ID={cluster_id}")
114+
print("========================")
115+
116+
117+
async def list_servers(*, client: AsyncGcore, cluster_id: str) -> None:
118+
print("\n=== LIST GPU VIRTUAL CLUSTER SERVERS ===")
119+
servers = await client.cloud.gpu_virtual.clusters.servers.list(cluster_id=cluster_id)
120+
for count, server in enumerate(servers.results, 1):
121+
print(f" {count}. Server: ID={server.id}, name={server.name}, status={server.status}")
122+
print("========================")
123+
124+
125+
async def list_flavors(*, client: AsyncGcore) -> List[GPUVirtualFlavor]:
126+
print("\n=== LIST GPU VIRTUAL FLAVORS ===")
127+
flavors = await client.cloud.gpu_virtual.clusters.flavors.list()
128+
_print_flavor_details(flavors.results)
129+
print("========================")
130+
return flavors.results
131+
132+
133+
async def list_images(*, client: AsyncGcore) -> List[GPUImage]:
134+
print("\n=== LIST GPU VIRTUAL IMAGES ===")
135+
images = await client.cloud.gpu_virtual.clusters.images.list()
136+
_print_image_details(images.results)
137+
print("========================")
138+
return images.results
139+
140+
141+
def _print_flavor_details(flavors: List[GPUVirtualFlavor]) -> None:
142+
display_count = 3
143+
if len(flavors) < display_count:
144+
display_count = len(flavors)
145+
146+
for i in range(display_count):
147+
flavor = flavors[i]
148+
print(f" {i + 1}. Flavor: name={flavor.name}")
149+
print(f" Capacity: {flavor.capacity}")
150+
status = "AVAILABLE"
151+
if flavor.disabled:
152+
status = "DISABLED"
153+
print(f" Status: {status}")
154+
print()
155+
156+
if len(flavors) > display_count:
157+
print(f" ... and {len(flavors) - display_count} more flavors")
158+
159+
160+
def _print_image_details(images: List[GPUImage]) -> None:
161+
display_count = 3
162+
if len(images) < display_count:
163+
display_count = len(images)
164+
165+
for i in range(display_count):
166+
img = images[i]
167+
print(f" {i + 1}. Image ID: {img.id}, name: {img.name}, OS type: {img.os_type}, status: {img.status}")
168+
169+
if len(images) > display_count:
170+
print(f" ... and {len(images) - display_count} more images")
171+
172+
173+
def _get_first_available_flavor(flavors: List[GPUVirtualFlavor]) -> str:
174+
available_flavors = [f for f in flavors if not f.disabled and f.capacity > 0]
175+
if not available_flavors:
176+
raise ValueError("No available flavors with capacity found")
177+
selected = available_flavors[0]
178+
print(f"Selected flavor: {selected.name} (Capacity: {selected.capacity})")
179+
return selected.name
180+
181+
182+
def _get_first_image(images: List[GPUImage]) -> str:
183+
if not images:
184+
raise ValueError("No images found")
185+
selected = images[0]
186+
print(f"Selected image: {selected.name} (ID: {selected.id})")
187+
return selected.id
188+
189+
190+
if __name__ == "__main__":
191+
asyncio.run(main())

0 commit comments

Comments
 (0)