From 99955b9208b2810fcbafbf5f8d941b5981faebbe Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 11:29:53 +0000 Subject: [PATCH 1/6] chore(package): drop Python 3.8 support --- README.md | 4 ++-- pyproject.toml | 5 ++--- src/gcore/_utils/_sync.py | 34 +++------------------------------- 3 files changed, 7 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index bcba1e06..3c62683a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![PyPI version](https://img.shields.io/pypi/v/gcore.svg?label=pypi%20(stable))[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FG-Core%2Fgcore-python.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FG-Core%2Fgcore-python?ref=badge_shield) ](https://pypi.org/project/gcore/) -The Gcore Python library provides convenient access to the Gcore REST API from any Python 3.8+ +The Gcore Python library provides convenient access to the Gcore REST API from any Python 3.9+ application. The library includes type definitions for all request params and response fields, and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx). @@ -479,7 +479,7 @@ print(gcore.__version__) ## Requirements -Python 3.8 or higher. +Python 3.9 or higher. ## Contributing diff --git a/pyproject.toml b/pyproject.toml index e1d7434a..6aeac16c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,11 +15,10 @@ dependencies = [ "distro>=1.7.0, <2", "sniffio", ] -requires-python = ">= 3.8" +requires-python = ">= 3.9" classifiers = [ "Typing :: Typed", "Intended Audience :: Developers", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -143,7 +142,7 @@ filterwarnings = [ # there are a couple of flags that are still disabled by # default in strict mode as they are experimental and niche. typeCheckingMode = "strict" -pythonVersion = "3.8" +pythonVersion = "3.9" exclude = [ "_dev", diff --git a/src/gcore/_utils/_sync.py b/src/gcore/_utils/_sync.py index ad7ec71b..f6027c18 100644 --- a/src/gcore/_utils/_sync.py +++ b/src/gcore/_utils/_sync.py @@ -1,10 +1,8 @@ from __future__ import annotations -import sys import asyncio import functools -import contextvars -from typing import Any, TypeVar, Callable, Awaitable +from typing import TypeVar, Callable, Awaitable from typing_extensions import ParamSpec import anyio @@ -15,34 +13,11 @@ T_ParamSpec = ParamSpec("T_ParamSpec") -if sys.version_info >= (3, 9): - _asyncio_to_thread = asyncio.to_thread -else: - # backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread - # for Python 3.8 support - async def _asyncio_to_thread( - func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs - ) -> Any: - """Asynchronously run function *func* in a separate thread. - - Any *args and **kwargs supplied for this function are directly passed - to *func*. Also, the current :class:`contextvars.Context` is propagated, - allowing context variables from the main thread to be accessed in the - separate thread. - - Returns a coroutine that can be awaited to get the eventual result of *func*. - """ - loop = asyncio.events.get_running_loop() - ctx = contextvars.copy_context() - func_call = functools.partial(ctx.run, func, *args, **kwargs) - return await loop.run_in_executor(None, func_call) - - async def to_thread( func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs ) -> T_Retval: if sniffio.current_async_library() == "asyncio": - return await _asyncio_to_thread(func, *args, **kwargs) + return await asyncio.to_thread(func, *args, **kwargs) return await anyio.to_thread.run_sync( functools.partial(func, *args, **kwargs), @@ -53,10 +28,7 @@ async def to_thread( def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: """ Take a blocking function and create an async one that receives the same - positional and keyword arguments. For python version 3.9 and above, it uses - asyncio.to_thread to run the function in a separate thread. For python version - 3.8, it uses locally defined copy of the asyncio.to_thread function which was - introduced in python 3.9. + positional and keyword arguments. Usage: From 90bfffeed8b28a360e1489162d4dc6a28745b411 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 13:36:28 +0000 Subject: [PATCH 2/6] fix: compat with Python 3.14 --- src/gcore/_models.py | 11 ++++++++--- tests/test_models.py | 8 ++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/gcore/_models.py b/src/gcore/_models.py index 6a3cd1d2..fcec2cf9 100644 --- a/src/gcore/_models.py +++ b/src/gcore/_models.py @@ -2,6 +2,7 @@ import os import inspect +import weakref from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast from datetime import date, datetime from typing_extensions import ( @@ -573,6 +574,9 @@ class CachedDiscriminatorType(Protocol): __discriminator__: DiscriminatorDetails +DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary() + + class DiscriminatorDetails: field_name: str """The name of the discriminator field in the variant class, e.g. @@ -615,8 +619,9 @@ def __init__( def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None: - if isinstance(union, CachedDiscriminatorType): - return union.__discriminator__ + cached = DISCRIMINATOR_CACHE.get(union) + if cached is not None: + return cached discriminator_field_name: str | None = None @@ -669,7 +674,7 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, discriminator_field=discriminator_field_name, discriminator_alias=discriminator_alias, ) - cast(CachedDiscriminatorType, union).__discriminator__ = details + DISCRIMINATOR_CACHE.setdefault(union, details) return details diff --git a/tests/test_models.py b/tests/test_models.py index 7b21d517..d899cd02 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -9,7 +9,7 @@ from gcore._utils import PropertyInfo from gcore._compat import PYDANTIC_V1, parse_obj, model_dump, model_json -from gcore._models import BaseModel, construct_type +from gcore._models import DISCRIMINATOR_CACHE, BaseModel, construct_type class BasicModel(BaseModel): @@ -809,7 +809,7 @@ class B(BaseModel): UnionType = cast(Any, Union[A, B]) - assert not hasattr(UnionType, "__discriminator__") + assert not DISCRIMINATOR_CACHE.get(UnionType) m = construct_type( value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) @@ -818,7 +818,7 @@ class B(BaseModel): assert m.type == "b" assert m.data == "foo" # type: ignore[comparison-overlap] - discriminator = UnionType.__discriminator__ + discriminator = DISCRIMINATOR_CACHE.get(UnionType) assert discriminator is not None m = construct_type( @@ -830,7 +830,7 @@ class B(BaseModel): # if the discriminator details object stays the same between invocations then # we hit the cache - assert UnionType.__discriminator__ is discriminator + assert DISCRIMINATOR_CACHE.get(UnionType) is discriminator @pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1") From 767fdd5d74846369e3331efba210aff12a43e802 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 06:14:13 +0000 Subject: [PATCH 3/6] feat(api): aggregated API specs update --- .stats.yml | 4 ++-- src/gcore/resources/cdn/audit_log.py | 8 ++++---- src/gcore/types/cdn/audit_log_list_params.py | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.stats.yml b/.stats.yml index 934a1512..11307508 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 618 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-29e2cc8d4eccb3e6657d61c17d200583cc2bd5bd90494b331881256e362d5873.yml -openapi_spec_hash: de6249d14f251ef0a008dd506e0e99ef +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-cf3ae8749ead0412e761136b7c81a9ca98dd46ca9ef76c1f700cb1ecc814630d.yml +openapi_spec_hash: d5b2b66116339bbe16ec0bbff5b6366f config_hash: db560bc3873a6441828babf34ae5f184 diff --git a/src/gcore/resources/cdn/audit_log.py b/src/gcore/resources/cdn/audit_log.py index f39a518f..a80c335e 100644 --- a/src/gcore/resources/cdn/audit_log.py +++ b/src/gcore/resources/cdn/audit_log.py @@ -99,9 +99,9 @@ def list( offset: Offset relative to the beginning of activity logs. - path: Path that a requested URL should contain. + path: Exact URL path. - remote_ip_address: IP address or part of it from which requests are sent. + remote_ip_address: Exact IP address from which requests are sent. status_code: Status code returned in the response. @@ -263,9 +263,9 @@ def list( offset: Offset relative to the beginning of activity logs. - path: Path that a requested URL should contain. + path: Exact URL path. - remote_ip_address: IP address or part of it from which requests are sent. + remote_ip_address: Exact IP address from which requests are sent. status_code: Status code returned in the response. diff --git a/src/gcore/types/cdn/audit_log_list_params.py b/src/gcore/types/cdn/audit_log_list_params.py index 95024e59..e70beede 100644 --- a/src/gcore/types/cdn/audit_log_list_params.py +++ b/src/gcore/types/cdn/audit_log_list_params.py @@ -50,10 +50,10 @@ class AuditLogListParams(TypedDict, total=False): """Offset relative to the beginning of activity logs.""" path: str - """Path that a requested URL should contain.""" + """Exact URL path.""" remote_ip_address: str - """IP address or part of it from which requests are sent.""" + """Exact IP address from which requests are sent.""" status_code: int """Status code returned in the response. From c406d97d04db03f5c4d105f2e9761e9b8c2d96c8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 13:50:59 +0000 Subject: [PATCH 4/6] feat(cloud): add support for GPU virtual clusters --- .stats.yml | 4 +- api.md | 81 + src/gcore/resources/cloud/__init__.py | 14 + src/gcore/resources/cloud/cloud.py | 32 + .../cloud/gpu_virtual_clusters/__init__.py | 89 + .../cloud/gpu_virtual_clusters/flavors.py | 211 +++ .../gpu_virtual_clusters.py | 1539 +++++++++++++++++ .../cloud/gpu_virtual_clusters/images.py | 580 +++++++ .../cloud/gpu_virtual_clusters/interfaces.py | 187 ++ .../cloud/gpu_virtual_clusters/servers.py | 506 ++++++ .../cloud/gpu_virtual_clusters/volumes.py | 187 ++ src/gcore/types/cloud/__init__.py | 6 + src/gcore/types/cloud/gpu_virtual_cluster.py | 189 ++ .../gpu_virtual_cluster_action_params.py | 122 ++ .../gpu_virtual_cluster_create_params.py | 213 +++ .../gpu_virtual_cluster_delete_params.py | 41 + .../cloud/gpu_virtual_cluster_list_params.py | 21 + .../gpu_virtual_cluster_update_params.py | 18 + .../cloud/gpu_virtual_clusters/__init__.py | 16 + .../flavor_list_params.py | 21 + .../gpu_virtual_cluster_server.py | 77 + .../gpu_virtual_cluster_server_list.py | 16 + .../gpu_virtual_cluster_volume.py | 64 + .../gpu_virtual_cluster_volume_list.py | 16 + .../gpu_virtual_flavor.py | 155 ++ .../gpu_virtual_flavor_list.py | 16 + .../gpu_virtual_interface.py | 190 ++ .../gpu_virtual_interface_list.py | 16 + .../image_upload_params.py | 56 + .../server_delete_params.py | 44 + .../server_list_params.py | 75 + .../cloud/gpu_virtual_clusters/__init__.py | 1 + .../gpu_virtual_clusters/test_flavors.py | 112 ++ .../cloud/gpu_virtual_clusters/test_images.py | 392 +++++ .../gpu_virtual_clusters/test_interfaces.py | 116 ++ .../gpu_virtual_clusters/test_servers.py | 302 ++++ .../gpu_virtual_clusters/test_volumes.py | 116 ++ .../cloud/test_gpu_virtual_clusters.py | 1294 ++++++++++++++ 38 files changed, 7133 insertions(+), 2 deletions(-) create mode 100644 src/gcore/resources/cloud/gpu_virtual_clusters/__init__.py create mode 100644 src/gcore/resources/cloud/gpu_virtual_clusters/flavors.py create mode 100644 src/gcore/resources/cloud/gpu_virtual_clusters/gpu_virtual_clusters.py create mode 100644 src/gcore/resources/cloud/gpu_virtual_clusters/images.py create mode 100644 src/gcore/resources/cloud/gpu_virtual_clusters/interfaces.py create mode 100644 src/gcore/resources/cloud/gpu_virtual_clusters/servers.py create mode 100644 src/gcore/resources/cloud/gpu_virtual_clusters/volumes.py create mode 100644 src/gcore/types/cloud/gpu_virtual_cluster.py create mode 100644 src/gcore/types/cloud/gpu_virtual_cluster_action_params.py create mode 100644 src/gcore/types/cloud/gpu_virtual_cluster_create_params.py create mode 100644 src/gcore/types/cloud/gpu_virtual_cluster_delete_params.py create mode 100644 src/gcore/types/cloud/gpu_virtual_cluster_list_params.py create mode 100644 src/gcore/types/cloud/gpu_virtual_cluster_update_params.py create mode 100644 src/gcore/types/cloud/gpu_virtual_clusters/__init__.py create mode 100644 src/gcore/types/cloud/gpu_virtual_clusters/flavor_list_params.py create mode 100644 src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_cluster_server.py create mode 100644 src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_cluster_server_list.py create mode 100644 src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_cluster_volume.py create mode 100644 src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_cluster_volume_list.py create mode 100644 src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_flavor.py create mode 100644 src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_flavor_list.py create mode 100644 src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_interface.py create mode 100644 src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_interface_list.py create mode 100644 src/gcore/types/cloud/gpu_virtual_clusters/image_upload_params.py create mode 100644 src/gcore/types/cloud/gpu_virtual_clusters/server_delete_params.py create mode 100644 src/gcore/types/cloud/gpu_virtual_clusters/server_list_params.py create mode 100644 tests/api_resources/cloud/gpu_virtual_clusters/__init__.py create mode 100644 tests/api_resources/cloud/gpu_virtual_clusters/test_flavors.py create mode 100644 tests/api_resources/cloud/gpu_virtual_clusters/test_images.py create mode 100644 tests/api_resources/cloud/gpu_virtual_clusters/test_interfaces.py create mode 100644 tests/api_resources/cloud/gpu_virtual_clusters/test_servers.py create mode 100644 tests/api_resources/cloud/gpu_virtual_clusters/test_volumes.py create mode 100644 tests/api_resources/cloud/test_gpu_virtual_clusters.py diff --git a/.stats.yml b/.stats.yml index 11307508..af8fa5c8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 618 +configured_endpoints: 633 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-cf3ae8749ead0412e761136b7c81a9ca98dd46ca9ef76c1f700cb1ecc814630d.yml openapi_spec_hash: d5b2b66116339bbe16ec0bbff5b6366f -config_hash: db560bc3873a6441828babf34ae5f184 +config_hash: e759f29c457a9e3ac5031e760c36594a diff --git a/api.md b/api.md index 180b482b..6158d8b8 100644 --- a/api.md +++ b/api.md @@ -826,6 +826,87 @@ Methods: - client.cloud.gpu_baremetal_clusters.images.get(image_id, \*, project_id, region_id) -> GPUImage - client.cloud.gpu_baremetal_clusters.images.upload(\*, project_id, region_id, \*\*params) -> TaskIDList +## GPUVirtualClusters + +Types: + +```python +from gcore.types.cloud import GPUVirtualCluster +``` + +Methods: + +- client.cloud.gpu_virtual_clusters.create(\*, project_id, region_id, \*\*params) -> TaskIDList +- client.cloud.gpu_virtual_clusters.update(cluster_id, \*, project_id, region_id, \*\*params) -> GPUVirtualCluster +- client.cloud.gpu_virtual_clusters.list(\*, project_id, region_id, \*\*params) -> SyncOffsetPage[GPUVirtualCluster] +- client.cloud.gpu_virtual_clusters.delete(cluster_id, \*, project_id, region_id, \*\*params) -> TaskIDList +- client.cloud.gpu_virtual_clusters.action(cluster_id, \*, project_id, region_id, \*\*params) -> TaskIDList +- client.cloud.gpu_virtual_clusters.get(cluster_id, \*, project_id, region_id) -> GPUVirtualCluster + +### Servers + +Types: + +```python +from gcore.types.cloud.gpu_virtual_clusters import ( + GPUVirtualClusterServer, + GPUVirtualClusterServerList, +) +``` + +Methods: + +- client.cloud.gpu_virtual_clusters.servers.list(cluster_id, \*, project_id, region_id, \*\*params) -> GPUVirtualClusterServerList +- client.cloud.gpu_virtual_clusters.servers.delete(server_id, \*, project_id, region_id, cluster_id, \*\*params) -> TaskIDList + +### Volumes + +Types: + +```python +from gcore.types.cloud.gpu_virtual_clusters import ( + GPUVirtualClusterVolume, + GPUVirtualClusterVolumeList, +) +``` + +Methods: + +- client.cloud.gpu_virtual_clusters.volumes.list(cluster_id, \*, project_id, region_id) -> GPUVirtualClusterVolumeList + +### Interfaces + +Types: + +```python +from gcore.types.cloud.gpu_virtual_clusters import GPUVirtualInterface, GPUVirtualInterfaceList +``` + +Methods: + +- client.cloud.gpu_virtual_clusters.interfaces.list(cluster_id, \*, project_id, region_id) -> GPUVirtualInterfaceList + +### Flavors + +Types: + +```python +from gcore.types.cloud.gpu_virtual_clusters import GPUVirtualFlavor, GPUVirtualFlavorList +``` + +Methods: + +- client.cloud.gpu_virtual_clusters.flavors.list(\*, project_id, region_id, \*\*params) -> GPUVirtualFlavorList + +### Images + +Methods: + +- client.cloud.gpu_virtual_clusters.images.list(\*, project_id, region_id) -> GPUImageList +- client.cloud.gpu_virtual_clusters.images.delete(image_id, \*, project_id, region_id) -> TaskIDList +- client.cloud.gpu_virtual_clusters.images.get(image_id, \*, project_id, region_id) -> GPUImage +- client.cloud.gpu_virtual_clusters.images.upload(\*, project_id, region_id, \*\*params) -> TaskIDList + ## Instances Types: diff --git a/src/gcore/resources/cloud/__init__.py b/src/gcore/resources/cloud/__init__.py index b430fc7c..6ae9dac7 100644 --- a/src/gcore/resources/cloud/__init__.py +++ b/src/gcore/resources/cloud/__init__.py @@ -216,6 +216,14 @@ BillingReservationsResourceWithStreamingResponse, AsyncBillingReservationsResourceWithStreamingResponse, ) +from .gpu_virtual_clusters import ( + GPUVirtualClustersResource, + AsyncGPUVirtualClustersResource, + GPUVirtualClustersResourceWithRawResponse, + AsyncGPUVirtualClustersResourceWithRawResponse, + GPUVirtualClustersResourceWithStreamingResponse, + AsyncGPUVirtualClustersResourceWithStreamingResponse, +) from .gpu_baremetal_clusters import ( GPUBaremetalClustersResource, AsyncGPUBaremetalClustersResource, @@ -352,6 +360,12 @@ "AsyncGPUBaremetalClustersResourceWithRawResponse", "GPUBaremetalClustersResourceWithStreamingResponse", "AsyncGPUBaremetalClustersResourceWithStreamingResponse", + "GPUVirtualClustersResource", + "AsyncGPUVirtualClustersResource", + "GPUVirtualClustersResourceWithRawResponse", + "AsyncGPUVirtualClustersResourceWithRawResponse", + "GPUVirtualClustersResourceWithStreamingResponse", + "AsyncGPUVirtualClustersResourceWithStreamingResponse", "InstancesResource", "AsyncInstancesResource", "InstancesResourceWithRawResponse", diff --git a/src/gcore/resources/cloud/cloud.py b/src/gcore/resources/cloud/cloud.py index 6583dc98..3f9ad996 100644 --- a/src/gcore/resources/cloud/cloud.py +++ b/src/gcore/resources/cloud/cloud.py @@ -212,6 +212,14 @@ ReservedFixedIPsResourceWithStreamingResponse, AsyncReservedFixedIPsResourceWithStreamingResponse, ) +from .gpu_virtual_clusters.gpu_virtual_clusters import ( + GPUVirtualClustersResource, + AsyncGPUVirtualClustersResource, + GPUVirtualClustersResourceWithRawResponse, + AsyncGPUVirtualClustersResourceWithRawResponse, + GPUVirtualClustersResourceWithStreamingResponse, + AsyncGPUVirtualClustersResourceWithStreamingResponse, +) from .gpu_baremetal_clusters.gpu_baremetal_clusters import ( GPUBaremetalClustersResource, AsyncGPUBaremetalClustersResource, @@ -316,6 +324,10 @@ def billing_reservations(self) -> BillingReservationsResource: def gpu_baremetal_clusters(self) -> GPUBaremetalClustersResource: return GPUBaremetalClustersResource(self._client) + @cached_property + def gpu_virtual_clusters(self) -> GPUVirtualClustersResource: + return GPUVirtualClustersResource(self._client) + @cached_property def instances(self) -> InstancesResource: return InstancesResource(self._client) @@ -452,6 +464,10 @@ def billing_reservations(self) -> AsyncBillingReservationsResource: def gpu_baremetal_clusters(self) -> AsyncGPUBaremetalClustersResource: return AsyncGPUBaremetalClustersResource(self._client) + @cached_property + def gpu_virtual_clusters(self) -> AsyncGPUVirtualClustersResource: + return AsyncGPUVirtualClustersResource(self._client) + @cached_property def instances(self) -> AsyncInstancesResource: return AsyncInstancesResource(self._client) @@ -591,6 +607,10 @@ def billing_reservations(self) -> BillingReservationsResourceWithRawResponse: def gpu_baremetal_clusters(self) -> GPUBaremetalClustersResourceWithRawResponse: return GPUBaremetalClustersResourceWithRawResponse(self._cloud.gpu_baremetal_clusters) + @cached_property + def gpu_virtual_clusters(self) -> GPUVirtualClustersResourceWithRawResponse: + return GPUVirtualClustersResourceWithRawResponse(self._cloud.gpu_virtual_clusters) + @cached_property def instances(self) -> InstancesResourceWithRawResponse: return InstancesResourceWithRawResponse(self._cloud.instances) @@ -711,6 +731,10 @@ def billing_reservations(self) -> AsyncBillingReservationsResourceWithRawRespons def gpu_baremetal_clusters(self) -> AsyncGPUBaremetalClustersResourceWithRawResponse: return AsyncGPUBaremetalClustersResourceWithRawResponse(self._cloud.gpu_baremetal_clusters) + @cached_property + def gpu_virtual_clusters(self) -> AsyncGPUVirtualClustersResourceWithRawResponse: + return AsyncGPUVirtualClustersResourceWithRawResponse(self._cloud.gpu_virtual_clusters) + @cached_property def instances(self) -> AsyncInstancesResourceWithRawResponse: return AsyncInstancesResourceWithRawResponse(self._cloud.instances) @@ -831,6 +855,10 @@ def billing_reservations(self) -> BillingReservationsResourceWithStreamingRespon def gpu_baremetal_clusters(self) -> GPUBaremetalClustersResourceWithStreamingResponse: return GPUBaremetalClustersResourceWithStreamingResponse(self._cloud.gpu_baremetal_clusters) + @cached_property + def gpu_virtual_clusters(self) -> GPUVirtualClustersResourceWithStreamingResponse: + return GPUVirtualClustersResourceWithStreamingResponse(self._cloud.gpu_virtual_clusters) + @cached_property def instances(self) -> InstancesResourceWithStreamingResponse: return InstancesResourceWithStreamingResponse(self._cloud.instances) @@ -951,6 +979,10 @@ def billing_reservations(self) -> AsyncBillingReservationsResourceWithStreamingR def gpu_baremetal_clusters(self) -> AsyncGPUBaremetalClustersResourceWithStreamingResponse: return AsyncGPUBaremetalClustersResourceWithStreamingResponse(self._cloud.gpu_baremetal_clusters) + @cached_property + def gpu_virtual_clusters(self) -> AsyncGPUVirtualClustersResourceWithStreamingResponse: + return AsyncGPUVirtualClustersResourceWithStreamingResponse(self._cloud.gpu_virtual_clusters) + @cached_property def instances(self) -> AsyncInstancesResourceWithStreamingResponse: return AsyncInstancesResourceWithStreamingResponse(self._cloud.instances) diff --git a/src/gcore/resources/cloud/gpu_virtual_clusters/__init__.py b/src/gcore/resources/cloud/gpu_virtual_clusters/__init__.py new file mode 100644 index 00000000..6957a34c --- /dev/null +++ b/src/gcore/resources/cloud/gpu_virtual_clusters/__init__.py @@ -0,0 +1,89 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .images import ( + ImagesResource, + AsyncImagesResource, + ImagesResourceWithRawResponse, + AsyncImagesResourceWithRawResponse, + ImagesResourceWithStreamingResponse, + AsyncImagesResourceWithStreamingResponse, +) +from .flavors import ( + FlavorsResource, + AsyncFlavorsResource, + FlavorsResourceWithRawResponse, + AsyncFlavorsResourceWithRawResponse, + FlavorsResourceWithStreamingResponse, + AsyncFlavorsResourceWithStreamingResponse, +) +from .servers import ( + ServersResource, + AsyncServersResource, + ServersResourceWithRawResponse, + AsyncServersResourceWithRawResponse, + ServersResourceWithStreamingResponse, + AsyncServersResourceWithStreamingResponse, +) +from .volumes import ( + VolumesResource, + AsyncVolumesResource, + VolumesResourceWithRawResponse, + AsyncVolumesResourceWithRawResponse, + VolumesResourceWithStreamingResponse, + AsyncVolumesResourceWithStreamingResponse, +) +from .interfaces import ( + InterfacesResource, + AsyncInterfacesResource, + InterfacesResourceWithRawResponse, + AsyncInterfacesResourceWithRawResponse, + InterfacesResourceWithStreamingResponse, + AsyncInterfacesResourceWithStreamingResponse, +) +from .gpu_virtual_clusters import ( + GPUVirtualClustersResource, + AsyncGPUVirtualClustersResource, + GPUVirtualClustersResourceWithRawResponse, + AsyncGPUVirtualClustersResourceWithRawResponse, + GPUVirtualClustersResourceWithStreamingResponse, + AsyncGPUVirtualClustersResourceWithStreamingResponse, +) + +__all__ = [ + "ServersResource", + "AsyncServersResource", + "ServersResourceWithRawResponse", + "AsyncServersResourceWithRawResponse", + "ServersResourceWithStreamingResponse", + "AsyncServersResourceWithStreamingResponse", + "VolumesResource", + "AsyncVolumesResource", + "VolumesResourceWithRawResponse", + "AsyncVolumesResourceWithRawResponse", + "VolumesResourceWithStreamingResponse", + "AsyncVolumesResourceWithStreamingResponse", + "InterfacesResource", + "AsyncInterfacesResource", + "InterfacesResourceWithRawResponse", + "AsyncInterfacesResourceWithRawResponse", + "InterfacesResourceWithStreamingResponse", + "AsyncInterfacesResourceWithStreamingResponse", + "FlavorsResource", + "AsyncFlavorsResource", + "FlavorsResourceWithRawResponse", + "AsyncFlavorsResourceWithRawResponse", + "FlavorsResourceWithStreamingResponse", + "AsyncFlavorsResourceWithStreamingResponse", + "ImagesResource", + "AsyncImagesResource", + "ImagesResourceWithRawResponse", + "AsyncImagesResourceWithRawResponse", + "ImagesResourceWithStreamingResponse", + "AsyncImagesResourceWithStreamingResponse", + "GPUVirtualClustersResource", + "AsyncGPUVirtualClustersResource", + "GPUVirtualClustersResourceWithRawResponse", + "AsyncGPUVirtualClustersResourceWithRawResponse", + "GPUVirtualClustersResourceWithStreamingResponse", + "AsyncGPUVirtualClustersResourceWithStreamingResponse", +] diff --git a/src/gcore/resources/cloud/gpu_virtual_clusters/flavors.py b/src/gcore/resources/cloud/gpu_virtual_clusters/flavors.py new file mode 100644 index 00000000..269932f2 --- /dev/null +++ b/src/gcore/resources/cloud/gpu_virtual_clusters/flavors.py @@ -0,0 +1,211 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.cloud.gpu_virtual_clusters import flavor_list_params +from ....types.cloud.gpu_virtual_clusters.gpu_virtual_flavor_list import GPUVirtualFlavorList + +__all__ = ["FlavorsResource", "AsyncFlavorsResource"] + + +class FlavorsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> FlavorsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers + """ + return FlavorsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> FlavorsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response + """ + return FlavorsResourceWithStreamingResponse(self) + + def list( + self, + *, + project_id: int | None = None, + region_id: int | None = None, + hide_disabled: bool | Omit = omit, + include_prices: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUVirtualFlavorList: + """ + List virtual GPU flavors + + Args: + project_id: Project ID + + region_id: Region ID + + hide_disabled: Set to `true` to remove the disabled flavors from the response. + + include_prices: Set to `true` if the response should include flavor prices. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + return self._get( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/flavors", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "hide_disabled": hide_disabled, + "include_prices": include_prices, + }, + flavor_list_params.FlavorListParams, + ), + ), + cast_to=GPUVirtualFlavorList, + ) + + +class AsyncFlavorsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncFlavorsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers + """ + return AsyncFlavorsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncFlavorsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response + """ + return AsyncFlavorsResourceWithStreamingResponse(self) + + async def list( + self, + *, + project_id: int | None = None, + region_id: int | None = None, + hide_disabled: bool | Omit = omit, + include_prices: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUVirtualFlavorList: + """ + List virtual GPU flavors + + Args: + project_id: Project ID + + region_id: Region ID + + hide_disabled: Set to `true` to remove the disabled flavors from the response. + + include_prices: Set to `true` if the response should include flavor prices. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + return await self._get( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/flavors", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "hide_disabled": hide_disabled, + "include_prices": include_prices, + }, + flavor_list_params.FlavorListParams, + ), + ), + cast_to=GPUVirtualFlavorList, + ) + + +class FlavorsResourceWithRawResponse: + def __init__(self, flavors: FlavorsResource) -> None: + self._flavors = flavors + + self.list = to_raw_response_wrapper( + flavors.list, + ) + + +class AsyncFlavorsResourceWithRawResponse: + def __init__(self, flavors: AsyncFlavorsResource) -> None: + self._flavors = flavors + + self.list = async_to_raw_response_wrapper( + flavors.list, + ) + + +class FlavorsResourceWithStreamingResponse: + def __init__(self, flavors: FlavorsResource) -> None: + self._flavors = flavors + + self.list = to_streamed_response_wrapper( + flavors.list, + ) + + +class AsyncFlavorsResourceWithStreamingResponse: + def __init__(self, flavors: AsyncFlavorsResource) -> None: + self._flavors = flavors + + self.list = async_to_streamed_response_wrapper( + flavors.list, + ) diff --git a/src/gcore/resources/cloud/gpu_virtual_clusters/gpu_virtual_clusters.py b/src/gcore/resources/cloud/gpu_virtual_clusters/gpu_virtual_clusters.py new file mode 100644 index 00000000..7711ec5a --- /dev/null +++ b/src/gcore/resources/cloud/gpu_virtual_clusters/gpu_virtual_clusters.py @@ -0,0 +1,1539 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal, overload + +import httpx + +from .images import ( + ImagesResource, + AsyncImagesResource, + ImagesResourceWithRawResponse, + AsyncImagesResourceWithRawResponse, + ImagesResourceWithStreamingResponse, + AsyncImagesResourceWithStreamingResponse, +) +from .flavors import ( + FlavorsResource, + AsyncFlavorsResource, + FlavorsResourceWithRawResponse, + AsyncFlavorsResourceWithRawResponse, + FlavorsResourceWithStreamingResponse, + AsyncFlavorsResourceWithStreamingResponse, +) +from .servers import ( + ServersResource, + AsyncServersResource, + ServersResourceWithRawResponse, + AsyncServersResourceWithRawResponse, + ServersResourceWithStreamingResponse, + AsyncServersResourceWithStreamingResponse, +) +from .volumes import ( + VolumesResource, + AsyncVolumesResource, + VolumesResourceWithRawResponse, + AsyncVolumesResourceWithRawResponse, + VolumesResourceWithStreamingResponse, + AsyncVolumesResourceWithStreamingResponse, +) +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import required_args, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from .interfaces import ( + InterfacesResource, + AsyncInterfacesResource, + InterfacesResourceWithRawResponse, + AsyncInterfacesResourceWithRawResponse, + InterfacesResourceWithStreamingResponse, + AsyncInterfacesResourceWithStreamingResponse, +) +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ....pagination import SyncOffsetPage, AsyncOffsetPage +from ....types.cloud import ( + gpu_virtual_cluster_list_params, + gpu_virtual_cluster_action_params, + gpu_virtual_cluster_create_params, + gpu_virtual_cluster_delete_params, + gpu_virtual_cluster_update_params, +) +from ...._base_client import AsyncPaginator, make_request_options +from ....types.cloud.task_id_list import TaskIDList +from ....types.cloud.gpu_virtual_cluster import GPUVirtualCluster +from ....types.cloud.tag_update_map_param import TagUpdateMapParam + +__all__ = ["GPUVirtualClustersResource", "AsyncGPUVirtualClustersResource"] + + +class GPUVirtualClustersResource(SyncAPIResource): + @cached_property + def servers(self) -> ServersResource: + return ServersResource(self._client) + + @cached_property + def volumes(self) -> VolumesResource: + return VolumesResource(self._client) + + @cached_property + def interfaces(self) -> InterfacesResource: + return InterfacesResource(self._client) + + @cached_property + def flavors(self) -> FlavorsResource: + return FlavorsResource(self._client) + + @cached_property + def images(self) -> ImagesResource: + return ImagesResource(self._client) + + @cached_property + def with_raw_response(self) -> GPUVirtualClustersResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers + """ + return GPUVirtualClustersResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> GPUVirtualClustersResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response + """ + return GPUVirtualClustersResourceWithStreamingResponse(self) + + def create( + self, + *, + project_id: int | None = None, + region_id: int | None = None, + flavor: str, + name: str, + servers_count: int, + servers_settings: gpu_virtual_cluster_create_params.ServersSettings, + tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """ + Create a new virtual GPU cluster with the specified configuration. + + Args: + project_id: Project ID + + region_id: Region ID + + flavor: Cluster flavor ID + + name: Cluster name + + servers_count: Number of servers in the cluster + + servers_settings: Configuration settings for the servers in the cluster + + tags: Key-value tags to associate with the resource. A tag is a key-value pair that + can be associated with a resource, enabling efficient filtering and grouping for + better organization and management. Some tags are read-only and cannot be + modified by the user. Tags are also integrated with cost reports, allowing cost + data to be filtered based on tag keys or values. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + return self._post( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters", + body=maybe_transform( + { + "flavor": flavor, + "name": name, + "servers_count": servers_count, + "servers_settings": servers_settings, + "tags": tags, + }, + gpu_virtual_cluster_create_params.GPUVirtualClusterCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TaskIDList, + ) + + def update( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUVirtualCluster: + """ + Update the name of an existing virtual GPU cluster. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + name: Cluster name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not cluster_id: + raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") + return self._patch( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters/{cluster_id}", + body=maybe_transform({"name": name}, gpu_virtual_cluster_update_params.GPUVirtualClusterUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GPUVirtualCluster, + ) + + def list( + self, + *, + project_id: int | None = None, + region_id: int | None = None, + limit: int | Omit = omit, + offset: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncOffsetPage[GPUVirtualCluster]: + """ + List all virtual GPU clusters in the specified project and region. + + Args: + project_id: Project ID + + region_id: Region ID + + limit: Limit of items on a single page + + offset: Offset in results list + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + return self._get_api_list( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters", + page=SyncOffsetPage[GPUVirtualCluster], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "offset": offset, + }, + gpu_virtual_cluster_list_params.GPUVirtualClusterListParams, + ), + ), + model=GPUVirtualCluster, + ) + + def delete( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + all_floating_ips: bool | Omit = omit, + all_reserved_fixed_ips: bool | Omit = omit, + all_volumes: bool | Omit = omit, + floating_ip_ids: SequenceNotStr[str] | Omit = omit, + reserved_fixed_ip_ids: SequenceNotStr[str] | Omit = omit, + volume_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """ + Delete a virtual GPU cluster and all its associated resources. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + all_floating_ips: Flag indicating whether the floating ips associated with server / cluster are + deleted + + all_reserved_fixed_ips: Flag indicating whether the reserved fixed ips associated with server / cluster + are deleted + + all_volumes: Flag indicating whether all attached volumes are deleted + + floating_ip_ids: Optional list of floating ips to be deleted + + reserved_fixed_ip_ids: Optional list of reserved fixed ips to be deleted + + volume_ids: Optional list of volumes to be deleted + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not cluster_id: + raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") + return self._delete( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters/{cluster_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "all_floating_ips": all_floating_ips, + "all_reserved_fixed_ips": all_reserved_fixed_ips, + "all_volumes": all_volumes, + "floating_ip_ids": floating_ip_ids, + "reserved_fixed_ip_ids": reserved_fixed_ip_ids, + "volume_ids": volume_ids, + }, + gpu_virtual_cluster_delete_params.GPUVirtualClusterDeleteParams, + ), + ), + cast_to=TaskIDList, + ) + + @overload + def action( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + action: Literal["start"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """Perform a specific action on a virtual GPU cluster. + + Available actions: start, + stop, soft reboot, hard reboot, resize, update tags. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + action: Action name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def action( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + action: Literal["stop"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """Perform a specific action on a virtual GPU cluster. + + Available actions: start, + stop, soft reboot, hard reboot, resize, update tags. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + action: Action name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def action( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + action: Literal["soft_reboot"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """Perform a specific action on a virtual GPU cluster. + + Available actions: start, + stop, soft reboot, hard reboot, resize, update tags. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + action: Action name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def action( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + action: Literal["hard_reboot"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """Perform a specific action on a virtual GPU cluster. + + Available actions: start, + stop, soft reboot, hard reboot, resize, update tags. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + action: Action name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def action( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + action: Literal["update_tags"], + tags: Optional[TagUpdateMapParam], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """Perform a specific action on a virtual GPU cluster. + + Available actions: start, + stop, soft reboot, hard reboot, resize, update tags. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + action: Action name + + tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide + key-value pairs to add or update tags. Set tag values to `null` to remove tags. + Unspecified tags remain unchanged. Read-only tags are always preserved and + cannot be modified. + + **Examples:** + + - **Add/update tags:** + `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or + updates existing ones. + - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Remove all tags:** `{'tags': null}` removes all user-managed tags (read-only + tags are preserved). + - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates + specified tags. + - **Mixed operations:** + `{'tags': {'environment': 'production', '`cost_center`': 'engineering', '`deprecated_tag`': null}}` + adds/updates 'environment' and '`cost_center`' while removing + '`deprecated_tag`', preserving other existing tags. + - **Replace all:** first delete existing tags with null values, then add new + ones in the same request. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def action( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + action: Literal["resize"], + servers_count: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """Perform a specific action on a virtual GPU cluster. + + Available actions: start, + stop, soft reboot, hard reboot, resize, update tags. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + action: Action name + + servers_count: Requested servers count + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["action"], ["action", "tags"], ["action", "servers_count"]) + def action( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + action: Literal["start"] + | Literal["stop"] + | Literal["soft_reboot"] + | Literal["hard_reboot"] + | Literal["update_tags"] + | Literal["resize"], + tags: Optional[TagUpdateMapParam] | Omit = omit, + servers_count: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not cluster_id: + raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") + return self._post( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters/{cluster_id}/action", + body=maybe_transform( + { + "action": action, + "tags": tags, + "servers_count": servers_count, + }, + gpu_virtual_cluster_action_params.GPUVirtualClusterActionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TaskIDList, + ) + + def get( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUVirtualCluster: + """ + Get detailed information about a specific virtual GPU cluster. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not cluster_id: + raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") + return self._get( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters/{cluster_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GPUVirtualCluster, + ) + + +class AsyncGPUVirtualClustersResource(AsyncAPIResource): + @cached_property + def servers(self) -> AsyncServersResource: + return AsyncServersResource(self._client) + + @cached_property + def volumes(self) -> AsyncVolumesResource: + return AsyncVolumesResource(self._client) + + @cached_property + def interfaces(self) -> AsyncInterfacesResource: + return AsyncInterfacesResource(self._client) + + @cached_property + def flavors(self) -> AsyncFlavorsResource: + return AsyncFlavorsResource(self._client) + + @cached_property + def images(self) -> AsyncImagesResource: + return AsyncImagesResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncGPUVirtualClustersResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers + """ + return AsyncGPUVirtualClustersResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncGPUVirtualClustersResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response + """ + return AsyncGPUVirtualClustersResourceWithStreamingResponse(self) + + async def create( + self, + *, + project_id: int | None = None, + region_id: int | None = None, + flavor: str, + name: str, + servers_count: int, + servers_settings: gpu_virtual_cluster_create_params.ServersSettings, + tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """ + Create a new virtual GPU cluster with the specified configuration. + + Args: + project_id: Project ID + + region_id: Region ID + + flavor: Cluster flavor ID + + name: Cluster name + + servers_count: Number of servers in the cluster + + servers_settings: Configuration settings for the servers in the cluster + + tags: Key-value tags to associate with the resource. A tag is a key-value pair that + can be associated with a resource, enabling efficient filtering and grouping for + better organization and management. Some tags are read-only and cannot be + modified by the user. Tags are also integrated with cost reports, allowing cost + data to be filtered based on tag keys or values. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + return await self._post( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters", + body=await async_maybe_transform( + { + "flavor": flavor, + "name": name, + "servers_count": servers_count, + "servers_settings": servers_settings, + "tags": tags, + }, + gpu_virtual_cluster_create_params.GPUVirtualClusterCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TaskIDList, + ) + + async def update( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUVirtualCluster: + """ + Update the name of an existing virtual GPU cluster. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + name: Cluster name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not cluster_id: + raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") + return await self._patch( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters/{cluster_id}", + body=await async_maybe_transform( + {"name": name}, gpu_virtual_cluster_update_params.GPUVirtualClusterUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GPUVirtualCluster, + ) + + def list( + self, + *, + project_id: int | None = None, + region_id: int | None = None, + limit: int | Omit = omit, + offset: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[GPUVirtualCluster, AsyncOffsetPage[GPUVirtualCluster]]: + """ + List all virtual GPU clusters in the specified project and region. + + Args: + project_id: Project ID + + region_id: Region ID + + limit: Limit of items on a single page + + offset: Offset in results list + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + return self._get_api_list( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters", + page=AsyncOffsetPage[GPUVirtualCluster], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "offset": offset, + }, + gpu_virtual_cluster_list_params.GPUVirtualClusterListParams, + ), + ), + model=GPUVirtualCluster, + ) + + async def delete( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + all_floating_ips: bool | Omit = omit, + all_reserved_fixed_ips: bool | Omit = omit, + all_volumes: bool | Omit = omit, + floating_ip_ids: SequenceNotStr[str] | Omit = omit, + reserved_fixed_ip_ids: SequenceNotStr[str] | Omit = omit, + volume_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """ + Delete a virtual GPU cluster and all its associated resources. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + all_floating_ips: Flag indicating whether the floating ips associated with server / cluster are + deleted + + all_reserved_fixed_ips: Flag indicating whether the reserved fixed ips associated with server / cluster + are deleted + + all_volumes: Flag indicating whether all attached volumes are deleted + + floating_ip_ids: Optional list of floating ips to be deleted + + reserved_fixed_ip_ids: Optional list of reserved fixed ips to be deleted + + volume_ids: Optional list of volumes to be deleted + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not cluster_id: + raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") + return await self._delete( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters/{cluster_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "all_floating_ips": all_floating_ips, + "all_reserved_fixed_ips": all_reserved_fixed_ips, + "all_volumes": all_volumes, + "floating_ip_ids": floating_ip_ids, + "reserved_fixed_ip_ids": reserved_fixed_ip_ids, + "volume_ids": volume_ids, + }, + gpu_virtual_cluster_delete_params.GPUVirtualClusterDeleteParams, + ), + ), + cast_to=TaskIDList, + ) + + @overload + async def action( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + action: Literal["start"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """Perform a specific action on a virtual GPU cluster. + + Available actions: start, + stop, soft reboot, hard reboot, resize, update tags. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + action: Action name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def action( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + action: Literal["stop"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """Perform a specific action on a virtual GPU cluster. + + Available actions: start, + stop, soft reboot, hard reboot, resize, update tags. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + action: Action name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def action( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + action: Literal["soft_reboot"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """Perform a specific action on a virtual GPU cluster. + + Available actions: start, + stop, soft reboot, hard reboot, resize, update tags. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + action: Action name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def action( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + action: Literal["hard_reboot"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """Perform a specific action on a virtual GPU cluster. + + Available actions: start, + stop, soft reboot, hard reboot, resize, update tags. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + action: Action name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def action( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + action: Literal["update_tags"], + tags: Optional[TagUpdateMapParam], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """Perform a specific action on a virtual GPU cluster. + + Available actions: start, + stop, soft reboot, hard reboot, resize, update tags. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + action: Action name + + tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide + key-value pairs to add or update tags. Set tag values to `null` to remove tags. + Unspecified tags remain unchanged. Read-only tags are always preserved and + cannot be modified. + + **Examples:** + + - **Add/update tags:** + `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or + updates existing ones. + - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Remove all tags:** `{'tags': null}` removes all user-managed tags (read-only + tags are preserved). + - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates + specified tags. + - **Mixed operations:** + `{'tags': {'environment': 'production', '`cost_center`': 'engineering', '`deprecated_tag`': null}}` + adds/updates 'environment' and '`cost_center`' while removing + '`deprecated_tag`', preserving other existing tags. + - **Replace all:** first delete existing tags with null values, then add new + ones in the same request. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def action( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + action: Literal["resize"], + servers_count: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """Perform a specific action on a virtual GPU cluster. + + Available actions: start, + stop, soft reboot, hard reboot, resize, update tags. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + action: Action name + + servers_count: Requested servers count + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["action"], ["action", "tags"], ["action", "servers_count"]) + async def action( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + action: Literal["start"] + | Literal["stop"] + | Literal["soft_reboot"] + | Literal["hard_reboot"] + | Literal["update_tags"] + | Literal["resize"], + tags: Optional[TagUpdateMapParam] | Omit = omit, + servers_count: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not cluster_id: + raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") + return await self._post( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters/{cluster_id}/action", + body=await async_maybe_transform( + { + "action": action, + "tags": tags, + "servers_count": servers_count, + }, + gpu_virtual_cluster_action_params.GPUVirtualClusterActionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TaskIDList, + ) + + async def get( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUVirtualCluster: + """ + Get detailed information about a specific virtual GPU cluster. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not cluster_id: + raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") + return await self._get( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters/{cluster_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GPUVirtualCluster, + ) + + +class GPUVirtualClustersResourceWithRawResponse: + def __init__(self, gpu_virtual_clusters: GPUVirtualClustersResource) -> None: + self._gpu_virtual_clusters = gpu_virtual_clusters + + self.create = to_raw_response_wrapper( + gpu_virtual_clusters.create, + ) + self.update = to_raw_response_wrapper( + gpu_virtual_clusters.update, + ) + self.list = to_raw_response_wrapper( + gpu_virtual_clusters.list, + ) + self.delete = to_raw_response_wrapper( + gpu_virtual_clusters.delete, + ) + self.action = to_raw_response_wrapper( + gpu_virtual_clusters.action, + ) + self.get = to_raw_response_wrapper( + gpu_virtual_clusters.get, + ) + + @cached_property + def servers(self) -> ServersResourceWithRawResponse: + return ServersResourceWithRawResponse(self._gpu_virtual_clusters.servers) + + @cached_property + def volumes(self) -> VolumesResourceWithRawResponse: + return VolumesResourceWithRawResponse(self._gpu_virtual_clusters.volumes) + + @cached_property + def interfaces(self) -> InterfacesResourceWithRawResponse: + return InterfacesResourceWithRawResponse(self._gpu_virtual_clusters.interfaces) + + @cached_property + def flavors(self) -> FlavorsResourceWithRawResponse: + return FlavorsResourceWithRawResponse(self._gpu_virtual_clusters.flavors) + + @cached_property + def images(self) -> ImagesResourceWithRawResponse: + return ImagesResourceWithRawResponse(self._gpu_virtual_clusters.images) + + +class AsyncGPUVirtualClustersResourceWithRawResponse: + def __init__(self, gpu_virtual_clusters: AsyncGPUVirtualClustersResource) -> None: + self._gpu_virtual_clusters = gpu_virtual_clusters + + self.create = async_to_raw_response_wrapper( + gpu_virtual_clusters.create, + ) + self.update = async_to_raw_response_wrapper( + gpu_virtual_clusters.update, + ) + self.list = async_to_raw_response_wrapper( + gpu_virtual_clusters.list, + ) + self.delete = async_to_raw_response_wrapper( + gpu_virtual_clusters.delete, + ) + self.action = async_to_raw_response_wrapper( + gpu_virtual_clusters.action, + ) + self.get = async_to_raw_response_wrapper( + gpu_virtual_clusters.get, + ) + + @cached_property + def servers(self) -> AsyncServersResourceWithRawResponse: + return AsyncServersResourceWithRawResponse(self._gpu_virtual_clusters.servers) + + @cached_property + def volumes(self) -> AsyncVolumesResourceWithRawResponse: + return AsyncVolumesResourceWithRawResponse(self._gpu_virtual_clusters.volumes) + + @cached_property + def interfaces(self) -> AsyncInterfacesResourceWithRawResponse: + return AsyncInterfacesResourceWithRawResponse(self._gpu_virtual_clusters.interfaces) + + @cached_property + def flavors(self) -> AsyncFlavorsResourceWithRawResponse: + return AsyncFlavorsResourceWithRawResponse(self._gpu_virtual_clusters.flavors) + + @cached_property + def images(self) -> AsyncImagesResourceWithRawResponse: + return AsyncImagesResourceWithRawResponse(self._gpu_virtual_clusters.images) + + +class GPUVirtualClustersResourceWithStreamingResponse: + def __init__(self, gpu_virtual_clusters: GPUVirtualClustersResource) -> None: + self._gpu_virtual_clusters = gpu_virtual_clusters + + self.create = to_streamed_response_wrapper( + gpu_virtual_clusters.create, + ) + self.update = to_streamed_response_wrapper( + gpu_virtual_clusters.update, + ) + self.list = to_streamed_response_wrapper( + gpu_virtual_clusters.list, + ) + self.delete = to_streamed_response_wrapper( + gpu_virtual_clusters.delete, + ) + self.action = to_streamed_response_wrapper( + gpu_virtual_clusters.action, + ) + self.get = to_streamed_response_wrapper( + gpu_virtual_clusters.get, + ) + + @cached_property + def servers(self) -> ServersResourceWithStreamingResponse: + return ServersResourceWithStreamingResponse(self._gpu_virtual_clusters.servers) + + @cached_property + def volumes(self) -> VolumesResourceWithStreamingResponse: + return VolumesResourceWithStreamingResponse(self._gpu_virtual_clusters.volumes) + + @cached_property + def interfaces(self) -> InterfacesResourceWithStreamingResponse: + return InterfacesResourceWithStreamingResponse(self._gpu_virtual_clusters.interfaces) + + @cached_property + def flavors(self) -> FlavorsResourceWithStreamingResponse: + return FlavorsResourceWithStreamingResponse(self._gpu_virtual_clusters.flavors) + + @cached_property + def images(self) -> ImagesResourceWithStreamingResponse: + return ImagesResourceWithStreamingResponse(self._gpu_virtual_clusters.images) + + +class AsyncGPUVirtualClustersResourceWithStreamingResponse: + def __init__(self, gpu_virtual_clusters: AsyncGPUVirtualClustersResource) -> None: + self._gpu_virtual_clusters = gpu_virtual_clusters + + self.create = async_to_streamed_response_wrapper( + gpu_virtual_clusters.create, + ) + self.update = async_to_streamed_response_wrapper( + gpu_virtual_clusters.update, + ) + self.list = async_to_streamed_response_wrapper( + gpu_virtual_clusters.list, + ) + self.delete = async_to_streamed_response_wrapper( + gpu_virtual_clusters.delete, + ) + self.action = async_to_streamed_response_wrapper( + gpu_virtual_clusters.action, + ) + self.get = async_to_streamed_response_wrapper( + gpu_virtual_clusters.get, + ) + + @cached_property + def servers(self) -> AsyncServersResourceWithStreamingResponse: + return AsyncServersResourceWithStreamingResponse(self._gpu_virtual_clusters.servers) + + @cached_property + def volumes(self) -> AsyncVolumesResourceWithStreamingResponse: + return AsyncVolumesResourceWithStreamingResponse(self._gpu_virtual_clusters.volumes) + + @cached_property + def interfaces(self) -> AsyncInterfacesResourceWithStreamingResponse: + return AsyncInterfacesResourceWithStreamingResponse(self._gpu_virtual_clusters.interfaces) + + @cached_property + def flavors(self) -> AsyncFlavorsResourceWithStreamingResponse: + return AsyncFlavorsResourceWithStreamingResponse(self._gpu_virtual_clusters.flavors) + + @cached_property + def images(self) -> AsyncImagesResourceWithStreamingResponse: + return AsyncImagesResourceWithStreamingResponse(self._gpu_virtual_clusters.images) diff --git a/src/gcore/resources/cloud/gpu_virtual_clusters/images.py b/src/gcore/resources/cloud/gpu_virtual_clusters/images.py new file mode 100644 index 00000000..47315e2a --- /dev/null +++ b/src/gcore/resources/cloud/gpu_virtual_clusters/images.py @@ -0,0 +1,580 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal + +import httpx + +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.cloud.gpu_image import GPUImage +from ....types.cloud.task_id_list import TaskIDList +from ....types.cloud.gpu_image_list import GPUImageList +from ....types.cloud.gpu_virtual_clusters import image_upload_params + +__all__ = ["ImagesResource", "AsyncImagesResource"] + + +class ImagesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ImagesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers + """ + return ImagesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ImagesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response + """ + return ImagesResourceWithStreamingResponse(self) + + def list( + self, + *, + project_id: int | None = None, + region_id: int | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUImageList: + """ + List virtual GPU images + + Args: + project_id: Project ID + + region_id: Region ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + return self._get( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/images", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GPUImageList, + ) + + def delete( + self, + image_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """ + Delete virtual GPU image + + Args: + project_id: Project ID + + region_id: Region ID + + image_id: Image ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not image_id: + raise ValueError(f"Expected a non-empty value for `image_id` but received {image_id!r}") + return self._delete( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/images/{image_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TaskIDList, + ) + + def get( + self, + image_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUImage: + """ + Get virtual GPU image + + Args: + project_id: Project ID + + region_id: Region ID + + image_id: Image ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not image_id: + raise ValueError(f"Expected a non-empty value for `image_id` but received {image_id!r}") + return self._get( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/images/{image_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GPUImage, + ) + + def upload( + self, + *, + project_id: int | None = None, + region_id: int | None = None, + name: str, + url: str, + architecture: Optional[Literal["aarch64", "x86_64"]] | Omit = omit, + cow_format: bool | Omit = omit, + hw_firmware_type: Optional[Literal["bios", "uefi"]] | Omit = omit, + os_distro: Optional[str] | Omit = omit, + os_type: Optional[Literal["linux", "windows"]] | Omit = omit, + os_version: Optional[str] | Omit = omit, + ssh_key: Literal["allow", "deny", "required"] | Omit = omit, + tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """ + Upload new virtual GPU image + + Args: + project_id: Project ID + + region_id: Region ID + + name: Image name + + url: Image URL + + architecture: Image architecture type: aarch64, `x86_64` + + cow_format: When True, image cannot be deleted unless all volumes, created from it, are + deleted. + + hw_firmware_type: Specifies the type of firmware with which to boot the guest. + + os_distro: OS Distribution, i.e. Debian, CentOS, Ubuntu, CoreOS etc. + + os_type: The operating system installed on the image. Linux by default + + os_version: OS version, i.e. 19.04 (for Ubuntu) or 9.4 for Debian + + ssh_key: Permission to use a ssh key in instances + + tags: Key-value tags to associate with the resource. A tag is a key-value pair that + can be associated with a resource, enabling efficient filtering and grouping for + better organization and management. Some tags are read-only and cannot be + modified by the user. Tags are also integrated with cost reports, allowing cost + data to be filtered based on tag keys or values. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + return self._post( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/images", + body=maybe_transform( + { + "name": name, + "url": url, + "architecture": architecture, + "cow_format": cow_format, + "hw_firmware_type": hw_firmware_type, + "os_distro": os_distro, + "os_type": os_type, + "os_version": os_version, + "ssh_key": ssh_key, + "tags": tags, + }, + image_upload_params.ImageUploadParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TaskIDList, + ) + + +class AsyncImagesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncImagesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers + """ + return AsyncImagesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncImagesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response + """ + return AsyncImagesResourceWithStreamingResponse(self) + + async def list( + self, + *, + project_id: int | None = None, + region_id: int | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUImageList: + """ + List virtual GPU images + + Args: + project_id: Project ID + + region_id: Region ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + return await self._get( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/images", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GPUImageList, + ) + + async def delete( + self, + image_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """ + Delete virtual GPU image + + Args: + project_id: Project ID + + region_id: Region ID + + image_id: Image ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not image_id: + raise ValueError(f"Expected a non-empty value for `image_id` but received {image_id!r}") + return await self._delete( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/images/{image_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TaskIDList, + ) + + async def get( + self, + image_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUImage: + """ + Get virtual GPU image + + Args: + project_id: Project ID + + region_id: Region ID + + image_id: Image ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not image_id: + raise ValueError(f"Expected a non-empty value for `image_id` but received {image_id!r}") + return await self._get( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/images/{image_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GPUImage, + ) + + async def upload( + self, + *, + project_id: int | None = None, + region_id: int | None = None, + name: str, + url: str, + architecture: Optional[Literal["aarch64", "x86_64"]] | Omit = omit, + cow_format: bool | Omit = omit, + hw_firmware_type: Optional[Literal["bios", "uefi"]] | Omit = omit, + os_distro: Optional[str] | Omit = omit, + os_type: Optional[Literal["linux", "windows"]] | Omit = omit, + os_version: Optional[str] | Omit = omit, + ssh_key: Literal["allow", "deny", "required"] | Omit = omit, + tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """ + Upload new virtual GPU image + + Args: + project_id: Project ID + + region_id: Region ID + + name: Image name + + url: Image URL + + architecture: Image architecture type: aarch64, `x86_64` + + cow_format: When True, image cannot be deleted unless all volumes, created from it, are + deleted. + + hw_firmware_type: Specifies the type of firmware with which to boot the guest. + + os_distro: OS Distribution, i.e. Debian, CentOS, Ubuntu, CoreOS etc. + + os_type: The operating system installed on the image. Linux by default + + os_version: OS version, i.e. 19.04 (for Ubuntu) or 9.4 for Debian + + ssh_key: Permission to use a ssh key in instances + + tags: Key-value tags to associate with the resource. A tag is a key-value pair that + can be associated with a resource, enabling efficient filtering and grouping for + better organization and management. Some tags are read-only and cannot be + modified by the user. Tags are also integrated with cost reports, allowing cost + data to be filtered based on tag keys or values. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + return await self._post( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/images", + body=await async_maybe_transform( + { + "name": name, + "url": url, + "architecture": architecture, + "cow_format": cow_format, + "hw_firmware_type": hw_firmware_type, + "os_distro": os_distro, + "os_type": os_type, + "os_version": os_version, + "ssh_key": ssh_key, + "tags": tags, + }, + image_upload_params.ImageUploadParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TaskIDList, + ) + + +class ImagesResourceWithRawResponse: + def __init__(self, images: ImagesResource) -> None: + self._images = images + + self.list = to_raw_response_wrapper( + images.list, + ) + self.delete = to_raw_response_wrapper( + images.delete, + ) + self.get = to_raw_response_wrapper( + images.get, + ) + self.upload = to_raw_response_wrapper( + images.upload, + ) + + +class AsyncImagesResourceWithRawResponse: + def __init__(self, images: AsyncImagesResource) -> None: + self._images = images + + self.list = async_to_raw_response_wrapper( + images.list, + ) + self.delete = async_to_raw_response_wrapper( + images.delete, + ) + self.get = async_to_raw_response_wrapper( + images.get, + ) + self.upload = async_to_raw_response_wrapper( + images.upload, + ) + + +class ImagesResourceWithStreamingResponse: + def __init__(self, images: ImagesResource) -> None: + self._images = images + + self.list = to_streamed_response_wrapper( + images.list, + ) + self.delete = to_streamed_response_wrapper( + images.delete, + ) + self.get = to_streamed_response_wrapper( + images.get, + ) + self.upload = to_streamed_response_wrapper( + images.upload, + ) + + +class AsyncImagesResourceWithStreamingResponse: + def __init__(self, images: AsyncImagesResource) -> None: + self._images = images + + self.list = async_to_streamed_response_wrapper( + images.list, + ) + self.delete = async_to_streamed_response_wrapper( + images.delete, + ) + self.get = async_to_streamed_response_wrapper( + images.get, + ) + self.upload = async_to_streamed_response_wrapper( + images.upload, + ) diff --git a/src/gcore/resources/cloud/gpu_virtual_clusters/interfaces.py b/src/gcore/resources/cloud/gpu_virtual_clusters/interfaces.py new file mode 100644 index 00000000..ca97cda6 --- /dev/null +++ b/src/gcore/resources/cloud/gpu_virtual_clusters/interfaces.py @@ -0,0 +1,187 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.cloud.gpu_virtual_clusters.gpu_virtual_interface_list import GPUVirtualInterfaceList + +__all__ = ["InterfacesResource", "AsyncInterfacesResource"] + + +class InterfacesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> InterfacesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers + """ + return InterfacesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InterfacesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response + """ + return InterfacesResourceWithStreamingResponse(self) + + def list( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUVirtualInterfaceList: + """ + List all network interfaces for servers in a virtual GPU cluster. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not cluster_id: + raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") + return self._get( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters/{cluster_id}/interfaces", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GPUVirtualInterfaceList, + ) + + +class AsyncInterfacesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncInterfacesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers + """ + return AsyncInterfacesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInterfacesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response + """ + return AsyncInterfacesResourceWithStreamingResponse(self) + + async def list( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUVirtualInterfaceList: + """ + List all network interfaces for servers in a virtual GPU cluster. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not cluster_id: + raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") + return await self._get( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters/{cluster_id}/interfaces", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GPUVirtualInterfaceList, + ) + + +class InterfacesResourceWithRawResponse: + def __init__(self, interfaces: InterfacesResource) -> None: + self._interfaces = interfaces + + self.list = to_raw_response_wrapper( + interfaces.list, + ) + + +class AsyncInterfacesResourceWithRawResponse: + def __init__(self, interfaces: AsyncInterfacesResource) -> None: + self._interfaces = interfaces + + self.list = async_to_raw_response_wrapper( + interfaces.list, + ) + + +class InterfacesResourceWithStreamingResponse: + def __init__(self, interfaces: InterfacesResource) -> None: + self._interfaces = interfaces + + self.list = to_streamed_response_wrapper( + interfaces.list, + ) + + +class AsyncInterfacesResourceWithStreamingResponse: + def __init__(self, interfaces: AsyncInterfacesResource) -> None: + self._interfaces = interfaces + + self.list = async_to_streamed_response_wrapper( + interfaces.list, + ) diff --git a/src/gcore/resources/cloud/gpu_virtual_clusters/servers.py b/src/gcore/resources/cloud/gpu_virtual_clusters/servers.py new file mode 100644 index 00000000..a0176570 --- /dev/null +++ b/src/gcore/resources/cloud/gpu_virtual_clusters/servers.py @@ -0,0 +1,506 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Literal + +import httpx + +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.cloud.task_id_list import TaskIDList +from ....types.cloud.gpu_virtual_clusters import server_list_params, server_delete_params +from ....types.cloud.gpu_virtual_clusters.gpu_virtual_cluster_server_list import GPUVirtualClusterServerList + +__all__ = ["ServersResource", "AsyncServersResource"] + + +class ServersResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ServersResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers + """ + return ServersResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ServersResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response + """ + return ServersResourceWithStreamingResponse(self) + + def list( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + changed_before: Union[str, datetime] | Omit = omit, + changed_since: Union[str, datetime] | Omit = omit, + ip_address: str | Omit = omit, + limit: int | Omit = omit, + name: str | Omit = omit, + offset: int | Omit = omit, + order_by: Literal["created_at.asc", "created_at.desc", "status.asc", "status.desc"] | Omit = omit, + status: Literal[ + "ACTIVE", + "BUILD", + "ERROR", + "HARD_REBOOT", + "MIGRATING", + "PAUSED", + "REBOOT", + "REBUILD", + "RESIZE", + "REVERT_RESIZE", + "SHELVED", + "SHELVED_OFFLOADED", + "SHUTOFF", + "SOFT_DELETED", + "SUSPENDED", + "VERIFY_RESIZE", + ] + | Omit = omit, + uuids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUVirtualClusterServerList: + """ + List all servers in a virtual GPU cluster. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + changed_before: Filters the results to include only servers whose last change timestamp is less + than the specified datetime. Format: ISO 8601. + + changed_since: Filters the results to include only servers whose last change timestamp is + greater than or equal to the specified datetime. Format: ISO 8601. + + ip_address: Filter servers by ip address. + + limit: Limit of items on a single page + + name: Filter servers by name. You can provide a full or partial name, servers with + matching names will be returned. For example, entering 'test' will return all + servers that contain 'test' in their name. + + offset: Offset in results list + + order_by: Order field + + status: Filters servers by status. + + uuids: Filter servers by uuid. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not cluster_id: + raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") + return self._get( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters/{cluster_id}/servers", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "changed_before": changed_before, + "changed_since": changed_since, + "ip_address": ip_address, + "limit": limit, + "name": name, + "offset": offset, + "order_by": order_by, + "status": status, + "uuids": uuids, + }, + server_list_params.ServerListParams, + ), + ), + cast_to=GPUVirtualClusterServerList, + ) + + def delete( + self, + server_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + cluster_id: str, + all_floating_ips: bool | Omit = omit, + all_reserved_fixed_ips: bool | Omit = omit, + all_volumes: bool | Omit = omit, + floating_ip_ids: SequenceNotStr[str] | Omit = omit, + reserved_fixed_ip_ids: SequenceNotStr[str] | Omit = omit, + volume_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """ + Delete a server from a virtual GPU cluster and its associated resources. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + server_id: Server unique identifier + + all_floating_ips: Flag indicating whether the floating ips associated with server / cluster are + deleted + + all_reserved_fixed_ips: Flag indicating whether the reserved fixed ips associated with server / cluster + are deleted + + all_volumes: Flag indicating whether all attached volumes are deleted + + floating_ip_ids: Optional list of floating ips to be deleted + + reserved_fixed_ip_ids: Optional list of reserved fixed ips to be deleted + + volume_ids: Optional list of volumes to be deleted + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not cluster_id: + raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") + if not server_id: + raise ValueError(f"Expected a non-empty value for `server_id` but received {server_id!r}") + return self._delete( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters/{cluster_id}/servers/{server_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "all_floating_ips": all_floating_ips, + "all_reserved_fixed_ips": all_reserved_fixed_ips, + "all_volumes": all_volumes, + "floating_ip_ids": floating_ip_ids, + "reserved_fixed_ip_ids": reserved_fixed_ip_ids, + "volume_ids": volume_ids, + }, + server_delete_params.ServerDeleteParams, + ), + ), + cast_to=TaskIDList, + ) + + +class AsyncServersResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncServersResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers + """ + return AsyncServersResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncServersResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response + """ + return AsyncServersResourceWithStreamingResponse(self) + + async def list( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + changed_before: Union[str, datetime] | Omit = omit, + changed_since: Union[str, datetime] | Omit = omit, + ip_address: str | Omit = omit, + limit: int | Omit = omit, + name: str | Omit = omit, + offset: int | Omit = omit, + order_by: Literal["created_at.asc", "created_at.desc", "status.asc", "status.desc"] | Omit = omit, + status: Literal[ + "ACTIVE", + "BUILD", + "ERROR", + "HARD_REBOOT", + "MIGRATING", + "PAUSED", + "REBOOT", + "REBUILD", + "RESIZE", + "REVERT_RESIZE", + "SHELVED", + "SHELVED_OFFLOADED", + "SHUTOFF", + "SOFT_DELETED", + "SUSPENDED", + "VERIFY_RESIZE", + ] + | Omit = omit, + uuids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUVirtualClusterServerList: + """ + List all servers in a virtual GPU cluster. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + changed_before: Filters the results to include only servers whose last change timestamp is less + than the specified datetime. Format: ISO 8601. + + changed_since: Filters the results to include only servers whose last change timestamp is + greater than or equal to the specified datetime. Format: ISO 8601. + + ip_address: Filter servers by ip address. + + limit: Limit of items on a single page + + name: Filter servers by name. You can provide a full or partial name, servers with + matching names will be returned. For example, entering 'test' will return all + servers that contain 'test' in their name. + + offset: Offset in results list + + order_by: Order field + + status: Filters servers by status. + + uuids: Filter servers by uuid. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not cluster_id: + raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") + return await self._get( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters/{cluster_id}/servers", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "changed_before": changed_before, + "changed_since": changed_since, + "ip_address": ip_address, + "limit": limit, + "name": name, + "offset": offset, + "order_by": order_by, + "status": status, + "uuids": uuids, + }, + server_list_params.ServerListParams, + ), + ), + cast_to=GPUVirtualClusterServerList, + ) + + async def delete( + self, + server_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + cluster_id: str, + all_floating_ips: bool | Omit = omit, + all_reserved_fixed_ips: bool | Omit = omit, + all_volumes: bool | Omit = omit, + floating_ip_ids: SequenceNotStr[str] | Omit = omit, + reserved_fixed_ip_ids: SequenceNotStr[str] | Omit = omit, + volume_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TaskIDList: + """ + Delete a server from a virtual GPU cluster and its associated resources. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + server_id: Server unique identifier + + all_floating_ips: Flag indicating whether the floating ips associated with server / cluster are + deleted + + all_reserved_fixed_ips: Flag indicating whether the reserved fixed ips associated with server / cluster + are deleted + + all_volumes: Flag indicating whether all attached volumes are deleted + + floating_ip_ids: Optional list of floating ips to be deleted + + reserved_fixed_ip_ids: Optional list of reserved fixed ips to be deleted + + volume_ids: Optional list of volumes to be deleted + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not cluster_id: + raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") + if not server_id: + raise ValueError(f"Expected a non-empty value for `server_id` but received {server_id!r}") + return await self._delete( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters/{cluster_id}/servers/{server_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "all_floating_ips": all_floating_ips, + "all_reserved_fixed_ips": all_reserved_fixed_ips, + "all_volumes": all_volumes, + "floating_ip_ids": floating_ip_ids, + "reserved_fixed_ip_ids": reserved_fixed_ip_ids, + "volume_ids": volume_ids, + }, + server_delete_params.ServerDeleteParams, + ), + ), + cast_to=TaskIDList, + ) + + +class ServersResourceWithRawResponse: + def __init__(self, servers: ServersResource) -> None: + self._servers = servers + + self.list = to_raw_response_wrapper( + servers.list, + ) + self.delete = to_raw_response_wrapper( + servers.delete, + ) + + +class AsyncServersResourceWithRawResponse: + def __init__(self, servers: AsyncServersResource) -> None: + self._servers = servers + + self.list = async_to_raw_response_wrapper( + servers.list, + ) + self.delete = async_to_raw_response_wrapper( + servers.delete, + ) + + +class ServersResourceWithStreamingResponse: + def __init__(self, servers: ServersResource) -> None: + self._servers = servers + + self.list = to_streamed_response_wrapper( + servers.list, + ) + self.delete = to_streamed_response_wrapper( + servers.delete, + ) + + +class AsyncServersResourceWithStreamingResponse: + def __init__(self, servers: AsyncServersResource) -> None: + self._servers = servers + + self.list = async_to_streamed_response_wrapper( + servers.list, + ) + self.delete = async_to_streamed_response_wrapper( + servers.delete, + ) diff --git a/src/gcore/resources/cloud/gpu_virtual_clusters/volumes.py b/src/gcore/resources/cloud/gpu_virtual_clusters/volumes.py new file mode 100644 index 00000000..390e7cf9 --- /dev/null +++ b/src/gcore/resources/cloud/gpu_virtual_clusters/volumes.py @@ -0,0 +1,187 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.cloud.gpu_virtual_clusters.gpu_virtual_cluster_volume_list import GPUVirtualClusterVolumeList + +__all__ = ["VolumesResource", "AsyncVolumesResource"] + + +class VolumesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> VolumesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers + """ + return VolumesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> VolumesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response + """ + return VolumesResourceWithStreamingResponse(self) + + def list( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUVirtualClusterVolumeList: + """ + List all volumes attached to servers in a virtual GPU cluster. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not cluster_id: + raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") + return self._get( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters/{cluster_id}/volumes", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GPUVirtualClusterVolumeList, + ) + + +class AsyncVolumesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncVolumesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/G-Core/gcore-python#accessing-raw-response-data-eg-headers + """ + return AsyncVolumesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncVolumesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/G-Core/gcore-python#with_streaming_response + """ + return AsyncVolumesResourceWithStreamingResponse(self) + + async def list( + self, + cluster_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GPUVirtualClusterVolumeList: + """ + List all volumes attached to servers in a virtual GPU cluster. + + Args: + project_id: Project ID + + region_id: Region ID + + cluster_id: Cluster unique identifier + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if project_id is None: + project_id = self._client._get_cloud_project_id_path_param() + if region_id is None: + region_id = self._client._get_cloud_region_id_path_param() + if not cluster_id: + raise ValueError(f"Expected a non-empty value for `cluster_id` but received {cluster_id!r}") + return await self._get( + f"/cloud/v3/gpu/virtual/{project_id}/{region_id}/clusters/{cluster_id}/volumes", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GPUVirtualClusterVolumeList, + ) + + +class VolumesResourceWithRawResponse: + def __init__(self, volumes: VolumesResource) -> None: + self._volumes = volumes + + self.list = to_raw_response_wrapper( + volumes.list, + ) + + +class AsyncVolumesResourceWithRawResponse: + def __init__(self, volumes: AsyncVolumesResource) -> None: + self._volumes = volumes + + self.list = async_to_raw_response_wrapper( + volumes.list, + ) + + +class VolumesResourceWithStreamingResponse: + def __init__(self, volumes: VolumesResource) -> None: + self._volumes = volumes + + self.list = to_streamed_response_wrapper( + volumes.list, + ) + + +class AsyncVolumesResourceWithStreamingResponse: + def __init__(self, volumes: AsyncVolumesResource) -> None: + self._volumes = volumes + + self.list = async_to_streamed_response_wrapper( + volumes.list, + ) diff --git a/src/gcore/types/cloud/__init__.py b/src/gcore/types/cloud/__init__.py index 80b12b29..e97ac01a 100644 --- a/src/gcore/types/cloud/__init__.py +++ b/src/gcore/types/cloud/__init__.py @@ -64,6 +64,7 @@ from .billing_reservation import BillingReservation as BillingReservation from .ddos_profile_status import DDOSProfileStatus as DDOSProfileStatus from .fixed_address_short import FixedAddressShort as FixedAddressShort +from .gpu_virtual_cluster import GPUVirtualCluster as GPUVirtualCluster from .interface_ip_family import InterfaceIPFamily as InterfaceIPFamily from .k8s_cluster_version import K8sClusterVersion as K8sClusterVersion from .network_list_params import NetworkListParams as NetworkListParams @@ -160,11 +161,16 @@ from .load_balancer_operating_status import LoadBalancerOperatingStatus as LoadBalancerOperatingStatus from .billing_reservation_list_params import BillingReservationListParams as BillingReservationListParams from .cost_report_get_detailed_params import CostReportGetDetailedParams as CostReportGetDetailedParams +from .gpu_virtual_cluster_list_params import GPUVirtualClusterListParams as GPUVirtualClusterListParams from .reserved_fixed_ip_create_params import ReservedFixedIPCreateParams as ReservedFixedIPCreateParams from .reserved_fixed_ip_update_params import ReservedFixedIPUpdateParams as ReservedFixedIPUpdateParams from .volume_attach_to_instance_params import VolumeAttachToInstanceParams as VolumeAttachToInstanceParams from .cost_report_get_aggregated_params import CostReportGetAggregatedParams as CostReportGetAggregatedParams from .gpu_baremetal_cluster_list_params import GPUBaremetalClusterListParams as GPUBaremetalClusterListParams +from .gpu_virtual_cluster_action_params import GPUVirtualClusterActionParams as GPUVirtualClusterActionParams +from .gpu_virtual_cluster_create_params import GPUVirtualClusterCreateParams as GPUVirtualClusterCreateParams +from .gpu_virtual_cluster_delete_params import GPUVirtualClusterDeleteParams as GPUVirtualClusterDeleteParams +from .gpu_virtual_cluster_update_params import GPUVirtualClusterUpdateParams as GPUVirtualClusterUpdateParams from .laas_index_retention_policy_param import LaasIndexRetentionPolicyParam as LaasIndexRetentionPolicyParam from .load_balancer_member_connectivity import LoadBalancerMemberConnectivity as LoadBalancerMemberConnectivity from .volume_detach_from_instance_params import VolumeDetachFromInstanceParams as VolumeDetachFromInstanceParams diff --git a/src/gcore/types/cloud/gpu_virtual_cluster.py b/src/gcore/types/cloud/gpu_virtual_cluster.py new file mode 100644 index 00000000..28d58c1e --- /dev/null +++ b/src/gcore/types/cloud/gpu_virtual_cluster.py @@ -0,0 +1,189 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from datetime import datetime +from typing_extensions import Literal, Annotated, TypeAlias + +from .tag import Tag +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "GPUVirtualCluster", + "ServersSettings", + "ServersSettingsFileShare", + "ServersSettingsInterface", + "ServersSettingsInterfaceExternalInterfaceOutputSerializer", + "ServersSettingsInterfaceSubnetInterfaceOutputSerializer", + "ServersSettingsInterfaceSubnetInterfaceOutputSerializerFloatingIP", + "ServersSettingsInterfaceAnySubnetInterfaceOutputSerializer", + "ServersSettingsInterfaceAnySubnetInterfaceOutputSerializerFloatingIP", + "ServersSettingsSecurityGroup", + "ServersSettingsVolume", +] + + +class ServersSettingsFileShare(BaseModel): + id: str + """Unique identifier of the file share in UUID format.""" + + mount_path: str + """Absolute mount path inside the system where the file share will be mounted.""" + + +class ServersSettingsInterfaceExternalInterfaceOutputSerializer(BaseModel): + ip_family: Literal["dual", "ipv4", "ipv6"] + """Which subnets should be selected: IPv4, IPv6, or use dual stack.""" + + name: Optional[str] = None + """Interface name""" + + type: Literal["external"] + + +class ServersSettingsInterfaceSubnetInterfaceOutputSerializerFloatingIP(BaseModel): + source: Literal["new"] + + +class ServersSettingsInterfaceSubnetInterfaceOutputSerializer(BaseModel): + floating_ip: Optional[ServersSettingsInterfaceSubnetInterfaceOutputSerializerFloatingIP] = None + """Floating IP config for this subnet attachment""" + + name: Optional[str] = None + """Interface name""" + + network_id: str + """Network ID the subnet belongs to. Port will be plugged in this network""" + + subnet_id: str + """Port is assigned an IP address from this subnet""" + + type: Literal["subnet"] + + +class ServersSettingsInterfaceAnySubnetInterfaceOutputSerializerFloatingIP(BaseModel): + source: Literal["new"] + + +class ServersSettingsInterfaceAnySubnetInterfaceOutputSerializer(BaseModel): + floating_ip: Optional[ServersSettingsInterfaceAnySubnetInterfaceOutputSerializerFloatingIP] = None + """Floating IP config for this subnet attachment""" + + ip_address: Optional[str] = None + """Fixed IP address""" + + ip_family: Literal["dual", "ipv4", "ipv6"] + """Which subnets should be selected: IPv4, IPv6, or use dual stack""" + + name: Optional[str] = None + """Interface name""" + + network_id: str + """Network ID the subnet belongs to. Port will be plugged in this network""" + + type: Literal["any_subnet"] + + +ServersSettingsInterface: TypeAlias = Annotated[ + Union[ + ServersSettingsInterfaceExternalInterfaceOutputSerializer, + ServersSettingsInterfaceSubnetInterfaceOutputSerializer, + ServersSettingsInterfaceAnySubnetInterfaceOutputSerializer, + ], + PropertyInfo(discriminator="type"), +] + + +class ServersSettingsSecurityGroup(BaseModel): + id: str + """Security group ID""" + + name: str + """Security group name""" + + +class ServersSettingsVolume(BaseModel): + boot_index: Optional[int] = None + """Boot index of the volume""" + + delete_on_termination: bool + """Flag indicating whether the volume is deleted on instance termination""" + + image_id: Optional[str] = None + """Image ID for the volume""" + + name: str + """Volume name""" + + size: int + """Volume size in GiB""" + + tags: List[Tag] + """List of key-value tags associated with the resource. + + A tag is a key-value pair that can be associated with a resource, enabling + efficient filtering and grouping for better organization and management. Some + tags are read-only and cannot be modified by the user. Tags are also integrated + with cost reports, allowing cost data to be filtered based on tag keys or + values. + """ + + type: Literal["cold", "ssd_hiiops", "ssd_local", "ssd_lowlatency", "standard", "ultra"] + """Volume type""" + + +class ServersSettings(BaseModel): + file_shares: List[ServersSettingsFileShare] + """List of file shares mounted across the cluster.""" + + interfaces: List[ServersSettingsInterface] + + security_groups: List[ServersSettingsSecurityGroup] + """Security groups""" + + ssh_key_name: Optional[str] = None + """SSH key name""" + + user_data: Optional[str] = None + """Optional custom user data""" + + volumes: List[ServersSettingsVolume] + """List of volumes""" + + +class GPUVirtualCluster(BaseModel): + id: str + """Cluster unique identifier""" + + created_at: datetime + """Cluster creation date time""" + + flavor: str + """Cluster flavor name""" + + name: str + """Cluster name""" + + servers_count: int + """Cluster servers count""" + + servers_ids: List[str] + """List of cluster nodes""" + + servers_settings: ServersSettings + + status: Literal["active", "deleting", "error", "new", "resizing"] + """Cluster status""" + + tags: List[Tag] + """List of key-value tags associated with the resource. + + A tag is a key-value pair that can be associated with a resource, enabling + efficient filtering and grouping for better organization and management. Some + tags are read-only and cannot be modified by the user. Tags are also integrated + with cost reports, allowing cost data to be filtered based on tag keys or + values. + """ + + updated_at: Optional[datetime] = None + """Cluster update date time""" diff --git a/src/gcore/types/cloud/gpu_virtual_cluster_action_params.py b/src/gcore/types/cloud/gpu_virtual_cluster_action_params.py new file mode 100644 index 00000000..0c5f99b8 --- /dev/null +++ b/src/gcore/types/cloud/gpu_virtual_cluster_action_params.py @@ -0,0 +1,122 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .tag_update_map_param import TagUpdateMapParam + +__all__ = [ + "GPUVirtualClusterActionParams", + "StartVirtualGPUClusterSerializer", + "StopVirtualGPUClusterSerializer", + "SoftRebootVirtualGPUClusterSerializer", + "HardRebootVirtualGPUClusterSerializer", + "UpdateTagsGPUClusterSerializer", + "ResizeVirtualGPUClusterSerializer", +] + + +class StartVirtualGPUClusterSerializer(TypedDict, total=False): + project_id: int + """Project ID""" + + region_id: int + """Region ID""" + + action: Required[Literal["start"]] + """Action name""" + + +class StopVirtualGPUClusterSerializer(TypedDict, total=False): + project_id: int + """Project ID""" + + region_id: int + """Region ID""" + + action: Required[Literal["stop"]] + """Action name""" + + +class SoftRebootVirtualGPUClusterSerializer(TypedDict, total=False): + project_id: int + """Project ID""" + + region_id: int + """Region ID""" + + action: Required[Literal["soft_reboot"]] + """Action name""" + + +class HardRebootVirtualGPUClusterSerializer(TypedDict, total=False): + project_id: int + """Project ID""" + + region_id: int + """Region ID""" + + action: Required[Literal["hard_reboot"]] + """Action name""" + + +class UpdateTagsGPUClusterSerializer(TypedDict, total=False): + project_id: int + """Project ID""" + + region_id: int + """Region ID""" + + action: Required[Literal["update_tags"]] + """Action name""" + + tags: Required[Optional[TagUpdateMapParam]] + """Update key-value tags using JSON Merge Patch semantics (RFC 7386). + + Provide key-value pairs to add or update tags. Set tag values to `null` to + remove tags. Unspecified tags remain unchanged. Read-only tags are always + preserved and cannot be modified. + + **Examples:** + + - **Add/update tags:** + `{'tags': {'environment': 'production', 'team': 'backend'}}` adds new tags or + updates existing ones. + - **Delete tags:** `{'tags': {'`old_tag`': null}}` removes specific tags. + - **Remove all tags:** `{'tags': null}` removes all user-managed tags (read-only + tags are preserved). + - **Partial update:** `{'tags': {'environment': 'staging'}}` only updates + specified tags. + - **Mixed operations:** + `{'tags': {'environment': 'production', '`cost_center`': 'engineering', '`deprecated_tag`': null}}` + adds/updates 'environment' and '`cost_center`' while removing + '`deprecated_tag`', preserving other existing tags. + - **Replace all:** first delete existing tags with null values, then add new + ones in the same request. + """ + + +class ResizeVirtualGPUClusterSerializer(TypedDict, total=False): + project_id: int + """Project ID""" + + region_id: int + """Region ID""" + + action: Required[Literal["resize"]] + """Action name""" + + servers_count: Required[int] + """Requested servers count""" + + +GPUVirtualClusterActionParams: TypeAlias = Union[ + StartVirtualGPUClusterSerializer, + StopVirtualGPUClusterSerializer, + SoftRebootVirtualGPUClusterSerializer, + HardRebootVirtualGPUClusterSerializer, + UpdateTagsGPUClusterSerializer, + ResizeVirtualGPUClusterSerializer, +] diff --git a/src/gcore/types/cloud/gpu_virtual_cluster_create_params.py b/src/gcore/types/cloud/gpu_virtual_cluster_create_params.py new file mode 100644 index 00000000..acf172b3 --- /dev/null +++ b/src/gcore/types/cloud/gpu_virtual_cluster_create_params.py @@ -0,0 +1,213 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = [ + "GPUVirtualClusterCreateParams", + "ServersSettings", + "ServersSettingsInterface", + "ServersSettingsInterfaceExternalInterfaceInputSerializer", + "ServersSettingsInterfaceSubnetInterfaceInputSerializer", + "ServersSettingsInterfaceSubnetInterfaceInputSerializerFloatingIP", + "ServersSettingsInterfaceAnySubnetInterfaceInputSerializer", + "ServersSettingsInterfaceAnySubnetInterfaceInputSerializerFloatingIP", + "ServersSettingsVolume", + "ServersSettingsVolumeNewVolumeInputSerializer", + "ServersSettingsVolumeImageVolumeInputSerializer", + "ServersSettingsCredentials", + "ServersSettingsFileShare", + "ServersSettingsSecurityGroup", +] + + +class GPUVirtualClusterCreateParams(TypedDict, total=False): + project_id: int + """Project ID""" + + region_id: int + """Region ID""" + + flavor: Required[str] + """Cluster flavor ID""" + + name: Required[str] + """Cluster name""" + + servers_count: Required[int] + """Number of servers in the cluster""" + + servers_settings: Required[ServersSettings] + """Configuration settings for the servers in the cluster""" + + tags: Dict[str, str] + """Key-value tags to associate with the resource. + + A tag is a key-value pair that can be associated with a resource, enabling + efficient filtering and grouping for better organization and management. Some + tags are read-only and cannot be modified by the user. Tags are also integrated + with cost reports, allowing cost data to be filtered based on tag keys or + values. + """ + + +class ServersSettingsInterfaceExternalInterfaceInputSerializer(TypedDict, total=False): + type: Required[Literal["external"]] + + ip_family: Literal["dual", "ipv4", "ipv6"] + """Which subnets should be selected: IPv4, IPv6, or use dual stack.""" + + name: str + """Interface name""" + + +class ServersSettingsInterfaceSubnetInterfaceInputSerializerFloatingIP(TypedDict, total=False): + source: Required[Literal["new"]] + + +class ServersSettingsInterfaceSubnetInterfaceInputSerializer(TypedDict, total=False): + network_id: Required[str] + """Network ID the subnet belongs to. Port will be plugged in this network""" + + subnet_id: Required[str] + """Port is assigned an IP address from this subnet""" + + type: Required[Literal["subnet"]] + + floating_ip: ServersSettingsInterfaceSubnetInterfaceInputSerializerFloatingIP + """Floating IP config for this subnet attachment""" + + name: str + """Interface name""" + + +class ServersSettingsInterfaceAnySubnetInterfaceInputSerializerFloatingIP(TypedDict, total=False): + source: Required[Literal["new"]] + + +class ServersSettingsInterfaceAnySubnetInterfaceInputSerializer(TypedDict, total=False): + network_id: Required[str] + """Network ID the subnet belongs to. Port will be plugged in this network""" + + type: Required[Literal["any_subnet"]] + + floating_ip: ServersSettingsInterfaceAnySubnetInterfaceInputSerializerFloatingIP + """Floating IP config for this subnet attachment""" + + ip_family: Literal["dual", "ipv4", "ipv6"] + """Which subnets should be selected: IPv4, IPv6, or use dual stack""" + + name: str + """Interface name""" + + +ServersSettingsInterface: TypeAlias = Union[ + ServersSettingsInterfaceExternalInterfaceInputSerializer, + ServersSettingsInterfaceSubnetInterfaceInputSerializer, + ServersSettingsInterfaceAnySubnetInterfaceInputSerializer, +] + + +class ServersSettingsVolumeNewVolumeInputSerializer(TypedDict, total=False): + boot_index: Required[int] + """Boot index of the volume""" + + name: Required[str] + """Volume name""" + + size: Required[int] + """Volume size in GiB""" + + source: Required[Literal["new"]] + + type: Required[Literal["cold", "ssd_hiiops", "ssd_local", "ssd_lowlatency", "standard", "ultra"]] + """Volume type""" + + delete_on_termination: bool + """Flag indicating whether the volume is deleted on instance termination""" + + tags: Dict[str, str] + """Tags associated with the volume""" + + +class ServersSettingsVolumeImageVolumeInputSerializer(TypedDict, total=False): + boot_index: Required[int] + """Boot index of the volume""" + + image_id: Required[str] + """Image ID for the volume""" + + name: Required[str] + """Volume name""" + + size: Required[int] + """Volume size in GiB""" + + source: Required[Literal["image"]] + + type: Required[Literal["cold", "ssd_hiiops", "ssd_local", "ssd_lowlatency", "standard", "ultra"]] + """Volume type""" + + delete_on_termination: bool + """Flag indicating whether the volume is deleted on instance termination""" + + tags: Dict[str, str] + """Tags associated with the volume""" + + +ServersSettingsVolume: TypeAlias = Union[ + ServersSettingsVolumeNewVolumeInputSerializer, ServersSettingsVolumeImageVolumeInputSerializer +] + + +class ServersSettingsCredentials(TypedDict, total=False): + password: str + """Used to set the password for the specified 'username' on Linux instances. + + If 'username' is not provided, the password is applied to the default user of + the image. Mutually exclusive with '`user_data`' - only one can be specified. + """ + + ssh_key_name: str + """ + Specifies the name of the SSH keypair, created via the + [/v1/`ssh_keys` endpoint](/docs/api-reference/cloud/ssh-keys/add-or-generate-ssh-key). + """ + + username: str + """The 'username' and 'password' fields create a new user on the system""" + + +class ServersSettingsFileShare(TypedDict, total=False): + id: Required[str] + """Unique identifier of the file share in UUID format.""" + + mount_path: Required[str] + """Absolute mount path inside the system where the file share will be mounted.""" + + +class ServersSettingsSecurityGroup(TypedDict, total=False): + id: Required[str] + """Resource ID""" + + +class ServersSettings(TypedDict, total=False): + interfaces: Required[Iterable[ServersSettingsInterface]] + """Subnet IPs and floating IPs""" + + volumes: Required[Iterable[ServersSettingsVolume]] + """List of volumes""" + + credentials: ServersSettingsCredentials + """Optional server access credentials""" + + file_shares: Iterable[ServersSettingsFileShare] + """List of file shares to be mounted across the cluster.""" + + security_groups: Iterable[ServersSettingsSecurityGroup] + """List of security groups UUIDs""" + + user_data: str + """Optional custom user data (Base64-encoded)""" diff --git a/src/gcore/types/cloud/gpu_virtual_cluster_delete_params.py b/src/gcore/types/cloud/gpu_virtual_cluster_delete_params.py new file mode 100644 index 00000000..3cd9988b --- /dev/null +++ b/src/gcore/types/cloud/gpu_virtual_cluster_delete_params.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["GPUVirtualClusterDeleteParams"] + + +class GPUVirtualClusterDeleteParams(TypedDict, total=False): + project_id: int + """Project ID""" + + region_id: int + """Region ID""" + + all_floating_ips: bool + """ + Flag indicating whether the floating ips associated with server / cluster are + deleted + """ + + all_reserved_fixed_ips: bool + """ + Flag indicating whether the reserved fixed ips associated with server / cluster + are deleted + """ + + all_volumes: bool + """Flag indicating whether all attached volumes are deleted""" + + floating_ip_ids: SequenceNotStr[str] + """Optional list of floating ips to be deleted""" + + reserved_fixed_ip_ids: SequenceNotStr[str] + """Optional list of reserved fixed ips to be deleted""" + + volume_ids: SequenceNotStr[str] + """Optional list of volumes to be deleted""" diff --git a/src/gcore/types/cloud/gpu_virtual_cluster_list_params.py b/src/gcore/types/cloud/gpu_virtual_cluster_list_params.py new file mode 100644 index 00000000..ff0dba49 --- /dev/null +++ b/src/gcore/types/cloud/gpu_virtual_cluster_list_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["GPUVirtualClusterListParams"] + + +class GPUVirtualClusterListParams(TypedDict, total=False): + project_id: int + """Project ID""" + + region_id: int + """Region ID""" + + limit: int + """Limit of items on a single page""" + + offset: int + """Offset in results list""" diff --git a/src/gcore/types/cloud/gpu_virtual_cluster_update_params.py b/src/gcore/types/cloud/gpu_virtual_cluster_update_params.py new file mode 100644 index 00000000..790bbf88 --- /dev/null +++ b/src/gcore/types/cloud/gpu_virtual_cluster_update_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["GPUVirtualClusterUpdateParams"] + + +class GPUVirtualClusterUpdateParams(TypedDict, total=False): + project_id: int + """Project ID""" + + region_id: int + """Region ID""" + + name: Required[str] + """Cluster name""" diff --git a/src/gcore/types/cloud/gpu_virtual_clusters/__init__.py b/src/gcore/types/cloud/gpu_virtual_clusters/__init__.py new file mode 100644 index 00000000..df222501 --- /dev/null +++ b/src/gcore/types/cloud/gpu_virtual_clusters/__init__.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .flavor_list_params import FlavorListParams as FlavorListParams +from .gpu_virtual_flavor import GPUVirtualFlavor as GPUVirtualFlavor +from .server_list_params import ServerListParams as ServerListParams +from .image_upload_params import ImageUploadParams as ImageUploadParams +from .server_delete_params import ServerDeleteParams as ServerDeleteParams +from .gpu_virtual_interface import GPUVirtualInterface as GPUVirtualInterface +from .gpu_virtual_flavor_list import GPUVirtualFlavorList as GPUVirtualFlavorList +from .gpu_virtual_cluster_server import GPUVirtualClusterServer as GPUVirtualClusterServer +from .gpu_virtual_cluster_volume import GPUVirtualClusterVolume as GPUVirtualClusterVolume +from .gpu_virtual_interface_list import GPUVirtualInterfaceList as GPUVirtualInterfaceList +from .gpu_virtual_cluster_server_list import GPUVirtualClusterServerList as GPUVirtualClusterServerList +from .gpu_virtual_cluster_volume_list import GPUVirtualClusterVolumeList as GPUVirtualClusterVolumeList diff --git a/src/gcore/types/cloud/gpu_virtual_clusters/flavor_list_params.py b/src/gcore/types/cloud/gpu_virtual_clusters/flavor_list_params.py new file mode 100644 index 00000000..febeb592 --- /dev/null +++ b/src/gcore/types/cloud/gpu_virtual_clusters/flavor_list_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["FlavorListParams"] + + +class FlavorListParams(TypedDict, total=False): + project_id: int + """Project ID""" + + region_id: int + """Region ID""" + + hide_disabled: bool + """Set to `true` to remove the disabled flavors from the response.""" + + include_prices: bool + """Set to `true` if the response should include flavor prices.""" diff --git a/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_cluster_server.py b/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_cluster_server.py new file mode 100644 index 00000000..b1b8b52f --- /dev/null +++ b/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_cluster_server.py @@ -0,0 +1,77 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from ..tag import Tag +from ...._models import BaseModel + +__all__ = ["GPUVirtualClusterServer", "SecurityGroup"] + + +class SecurityGroup(BaseModel): + id: str + """Security group ID""" + + name: str + """Security group name""" + + +class GPUVirtualClusterServer(BaseModel): + id: str + """Server unique identifier""" + + created_at: datetime + """Server creation date and time""" + + flavor: str + """Unique flavor identifier""" + + image_id: Optional[str] = None + """Server's image UUID""" + + ip_addresses: List[str] + """List of IP addresses""" + + name: str + """Server's name generated using cluster's name""" + + security_groups: List[SecurityGroup] + """Security groups""" + + ssh_key_name: Optional[str] = None + """SSH key pair assigned to the server""" + + status: Literal[ + "ACTIVE", + "BUILD", + "DELETED", + "ERROR", + "HARD_REBOOT", + "MIGRATING", + "PASSWORD", + "PAUSED", + "REBOOT", + "REBUILD", + "RESCUE", + "RESIZE", + "REVERT_RESIZE", + "SHELVED", + "SHELVED_OFFLOADED", + "SHUTOFF", + "SOFT_DELETED", + "SUSPENDED", + "UNKNOWN", + "VERIFY_RESIZE", + ] + """Current server status""" + + tags: List[Tag] + """User defined tags""" + + task_id: Optional[str] = None + """Identifier of the task currently modifying the GPU cluster""" + + updated_at: datetime + """Server update date and time""" diff --git a/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_cluster_server_list.py b/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_cluster_server_list.py new file mode 100644 index 00000000..d9cdad5a --- /dev/null +++ b/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_cluster_server_list.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ...._models import BaseModel +from .gpu_virtual_cluster_server import GPUVirtualClusterServer + +__all__ = ["GPUVirtualClusterServerList"] + + +class GPUVirtualClusterServerList(BaseModel): + count: int + """Number of objects""" + + results: List[GPUVirtualClusterServer] + """Objects""" diff --git a/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_cluster_volume.py b/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_cluster_volume.py new file mode 100644 index 00000000..06653cde --- /dev/null +++ b/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_cluster_volume.py @@ -0,0 +1,64 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from datetime import datetime +from typing_extensions import Literal + +from ..tag import Tag +from ...._models import BaseModel + +__all__ = ["GPUVirtualClusterVolume"] + + +class GPUVirtualClusterVolume(BaseModel): + id: str + """Volume unique identifier""" + + bootable: bool + """True if this is bootable volume""" + + created_at: datetime + """Volume creation date and time""" + + name: str + """User defined name""" + + root_fs: bool + """True if this volume contains root file system""" + + server_id: str + """Server UUID""" + + size: int + """Volume size in GiB""" + + status: Literal[ + "attaching", + "available", + "awaiting-transfer", + "backing-up", + "creating", + "deleting", + "detaching", + "downloading", + "error", + "error_backing-up", + "error_deleting", + "error_extending", + "error_restoring", + "extending", + "in-use", + "maintenance", + "reserved", + "restoring-backup", + "retyping", + "reverting", + "uploading", + ] + """Current volume status""" + + tags: List[Tag] + """User defined tags""" + + type: str + """Volume type""" diff --git a/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_cluster_volume_list.py b/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_cluster_volume_list.py new file mode 100644 index 00000000..feb8afbc --- /dev/null +++ b/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_cluster_volume_list.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ...._models import BaseModel +from .gpu_virtual_cluster_volume import GPUVirtualClusterVolume + +__all__ = ["GPUVirtualClusterVolumeList"] + + +class GPUVirtualClusterVolumeList(BaseModel): + count: int + """Number of objects""" + + results: List[GPUVirtualClusterVolume] + """Objects""" diff --git a/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_flavor.py b/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_flavor.py new file mode 100644 index 00000000..cd90cda1 --- /dev/null +++ b/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_flavor.py @@ -0,0 +1,155 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, TypeAlias + +from ...._models import BaseModel + +__all__ = [ + "GPUVirtualFlavor", + "GPUVirtualFlavorSerializerWithoutPrice", + "GPUVirtualFlavorSerializerWithoutPriceHardwareDescription", + "GPUVirtualFlavorSerializerWithoutPriceHardwareProperties", + "GPUVirtualFlavorSerializerWithoutPriceSupportedFeatures", + "GPUVirtualFlavorSerializerWithPrices", + "GPUVirtualFlavorSerializerWithPricesHardwareDescription", + "GPUVirtualFlavorSerializerWithPricesHardwareProperties", + "GPUVirtualFlavorSerializerWithPricesPrice", + "GPUVirtualFlavorSerializerWithPricesSupportedFeatures", +] + + +class GPUVirtualFlavorSerializerWithoutPriceHardwareDescription(BaseModel): + gpu: Optional[str] = None + """Human-readable GPU description""" + + local_storage: Optional[int] = None + """Local storage capacity in GiB""" + + ram: Optional[int] = None + """RAM size in MiB""" + + vcpus: Optional[int] = None + """Virtual CPU count""" + + +class GPUVirtualFlavorSerializerWithoutPriceHardwareProperties(BaseModel): + gpu_count: Optional[int] = None + """The total count of available GPUs.""" + + gpu_manufacturer: Optional[str] = None + """The manufacturer of the graphics processing GPU""" + + gpu_model: Optional[str] = None + """GPU model""" + + nic_eth: Optional[str] = None + """The configuration of the Ethernet ports""" + + nic_ib: Optional[str] = None + """The configuration of the InfiniBand ports""" + + +class GPUVirtualFlavorSerializerWithoutPriceSupportedFeatures(BaseModel): + security_groups: bool + + +class GPUVirtualFlavorSerializerWithoutPrice(BaseModel): + architecture: Optional[str] = None + """Flavor architecture type""" + + capacity: int + """Number of available instances of given flavor""" + + disabled: bool + """If the flavor is disabled, new resources cannot be created using this flavor.""" + + hardware_description: GPUVirtualFlavorSerializerWithoutPriceHardwareDescription + """Additional virtual hardware description""" + + hardware_properties: GPUVirtualFlavorSerializerWithoutPriceHardwareProperties + """Additional virtual hardware properties""" + + name: str + """Flavor name""" + + supported_features: GPUVirtualFlavorSerializerWithoutPriceSupportedFeatures + """Set of enabled features based on the flavor's type and configuration""" + + +class GPUVirtualFlavorSerializerWithPricesHardwareDescription(BaseModel): + gpu: Optional[str] = None + """Human-readable GPU description""" + + local_storage: Optional[int] = None + """Local storage capacity in GiB""" + + ram: Optional[int] = None + """RAM size in MiB""" + + vcpus: Optional[int] = None + """Virtual CPU count""" + + +class GPUVirtualFlavorSerializerWithPricesHardwareProperties(BaseModel): + gpu_count: Optional[int] = None + """The total count of available GPUs.""" + + gpu_manufacturer: Optional[str] = None + """The manufacturer of the graphics processing GPU""" + + gpu_model: Optional[str] = None + """GPU model""" + + nic_eth: Optional[str] = None + """The configuration of the Ethernet ports""" + + nic_ib: Optional[str] = None + """The configuration of the InfiniBand ports""" + + +class GPUVirtualFlavorSerializerWithPricesPrice(BaseModel): + currency_code: Optional[str] = None + """Currency code. Shown if the `include_prices` query parameter if set to true""" + + price_per_hour: Optional[float] = None + """Price per hour. Shown if the `include_prices` query parameter if set to true""" + + price_per_month: Optional[float] = None + """Price per month. Shown if the `include_prices` query parameter if set to true""" + + price_status: Optional[Literal["error", "hide", "show"]] = None + """Price status for the UI""" + + +class GPUVirtualFlavorSerializerWithPricesSupportedFeatures(BaseModel): + security_groups: bool + + +class GPUVirtualFlavorSerializerWithPrices(BaseModel): + architecture: Optional[str] = None + """Flavor architecture type""" + + capacity: int + """Number of available instances of given flavor""" + + disabled: bool + """If the flavor is disabled, new resources cannot be created using this flavor.""" + + hardware_description: GPUVirtualFlavorSerializerWithPricesHardwareDescription + """Additional virtual hardware description""" + + hardware_properties: GPUVirtualFlavorSerializerWithPricesHardwareProperties + """Additional virtual hardware properties""" + + name: str + """Flavor name""" + + price: GPUVirtualFlavorSerializerWithPricesPrice + """Flavor price.""" + + supported_features: GPUVirtualFlavorSerializerWithPricesSupportedFeatures + """Set of enabled features based on the flavor's type and configuration""" + + +GPUVirtualFlavor: TypeAlias = Union[GPUVirtualFlavorSerializerWithoutPrice, GPUVirtualFlavorSerializerWithPrices] diff --git a/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_flavor_list.py b/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_flavor_list.py new file mode 100644 index 00000000..fdfab090 --- /dev/null +++ b/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_flavor_list.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ...._models import BaseModel +from .gpu_virtual_flavor import GPUVirtualFlavor + +__all__ = ["GPUVirtualFlavorList"] + + +class GPUVirtualFlavorList(BaseModel): + count: int + """Number of objects""" + + results: List[GPUVirtualFlavor] + """Objects""" diff --git a/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_interface.py b/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_interface.py new file mode 100644 index 00000000..fbac34d7 --- /dev/null +++ b/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_interface.py @@ -0,0 +1,190 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from ..tag import Tag +from ..route import Route +from ...._models import BaseModel +from ..ip_version import IPVersion +from ..floating_ip_status import FloatingIPStatus + +__all__ = ["GPUVirtualInterface", "FloatingIP", "IPAssignment", "Network", "NetworkSubnet"] + + +class FloatingIP(BaseModel): + id: str + """Floating IP ID""" + + created_at: datetime + """Datetime when the floating IP was created""" + + fixed_ip_address: Optional[str] = None + """IP address of the port the floating IP is attached to""" + + floating_ip_address: Optional[str] = None + """IP Address of the floating IP""" + + port_id: Optional[str] = None + """Port ID the floating IP is attached to. + + The `fixed_ip_address` is the IP address of the port. + """ + + router_id: Optional[str] = None + """Router ID""" + + status: Optional[FloatingIPStatus] = None + """Floating IP status""" + + tags: List[Tag] + """List of key-value tags associated with the resource. + + A tag is a key-value pair that can be associated with a resource, enabling + efficient filtering and grouping for better organization and management. Some + tags are read-only and cannot be modified by the user. Tags are also integrated + with cost reports, allowing cost data to be filtered based on tag keys or + values. + """ + + updated_at: datetime + """Datetime when the floating IP was last updated""" + + +class IPAssignment(BaseModel): + ip_address: str + """The IP address assigned to the port from the specified subnet""" + + subnet_id: str + """ID of the subnet that allocated the IP""" + + +class NetworkSubnet(BaseModel): + id: str + """Subnet id.""" + + available_ips: Optional[int] = None + """Number of available ips in subnet""" + + cidr: str + """CIDR""" + + created_at: datetime + """Datetime when the subnet was created""" + + dns_nameservers: Optional[List[str]] = None + """List IP addresses of a DNS resolver reachable from the network""" + + enable_dhcp: bool + """Indicates whether DHCP is enabled for this subnet. + + If true, IP addresses will be assigned automatically + """ + + gateway_ip: Optional[str] = None + """Default GW IPv4 address, advertised in DHCP routes of this subnet. + + If null, no gateway is advertised by this subnet. + """ + + has_router: bool + """Deprecated. Always returns `false`.""" + + host_routes: Optional[List[Route]] = None + """List of custom static routes to advertise via DHCP.""" + + ip_version: IPVersion + """IP version used by the subnet (IPv4 or IPv6)""" + + name: str + """Subnet name""" + + network_id: str + """Network ID""" + + tags: List[Tag] + """List of key-value tags associated with the resource. + + A tag is a key-value pair that can be associated with a resource, enabling + efficient filtering and grouping for better organization and management. Some + tags are read-only and cannot be modified by the user. Tags are also integrated + with cost reports, allowing cost data to be filtered based on tag keys or + values. + """ + + total_ips: Optional[int] = None + """Total number of ips in subnet""" + + updated_at: datetime + """Datetime when the subnet was last updated""" + + +class Network(BaseModel): + id: str + """Network ID""" + + created_at: datetime + """Datetime when the network was created""" + + external: bool + """True if the network `router:external` attribute""" + + mtu: int + """MTU (maximum transmission unit)""" + + name: str + """Network name""" + + port_security_enabled: bool + """ + Indicates `port_security_enabled` status of all newly created in the network + ports. + """ + + segmentation_id: Optional[int] = None + """Id of network segment""" + + shared: bool + """True when the network is shared with your project by external owner""" + + subnets: Optional[List[NetworkSubnet]] = None + """List of subnetworks""" + + tags: List[Tag] + """List of key-value tags associated with the resource. + + A tag is a key-value pair that can be associated with a resource, enabling + efficient filtering and grouping for better organization and management. Some + tags are read-only and cannot be modified by the user. Tags are also integrated + with cost reports, allowing cost data to be filtered based on tag keys or + values. + """ + + type: str + """Network type (vlan, vxlan)""" + + updated_at: datetime + """Datetime when the network was last updated""" + + +class GPUVirtualInterface(BaseModel): + floating_ips: List[FloatingIP] + """Bodies of floatingips that are NAT-ing ips of this port""" + + ip_assignments: List[IPAssignment] + """IP addresses assigned to this port""" + + mac_address: Optional[str] = None + """MAC address of the virtual port""" + + network: Network + """Body of the network this port is attached to""" + + network_id: str + """ID of the network the port is attached to""" + + port_id: str + """ID of virtual ethernet port object""" + + port_security_enabled: bool + """Port security status""" diff --git a/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_interface_list.py b/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_interface_list.py new file mode 100644 index 00000000..ae39f4cf --- /dev/null +++ b/src/gcore/types/cloud/gpu_virtual_clusters/gpu_virtual_interface_list.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ...._models import BaseModel +from .gpu_virtual_interface import GPUVirtualInterface + +__all__ = ["GPUVirtualInterfaceList"] + + +class GPUVirtualInterfaceList(BaseModel): + count: int + """Number of objects""" + + results: List[GPUVirtualInterface] + """Objects""" diff --git a/src/gcore/types/cloud/gpu_virtual_clusters/image_upload_params.py b/src/gcore/types/cloud/gpu_virtual_clusters/image_upload_params.py new file mode 100644 index 00000000..c83f5687 --- /dev/null +++ b/src/gcore/types/cloud/gpu_virtual_clusters/image_upload_params.py @@ -0,0 +1,56 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ImageUploadParams"] + + +class ImageUploadParams(TypedDict, total=False): + project_id: int + """Project ID""" + + region_id: int + """Region ID""" + + name: Required[str] + """Image name""" + + url: Required[str] + """Image URL""" + + architecture: Optional[Literal["aarch64", "x86_64"]] + """Image architecture type: aarch64, `x86_64`""" + + cow_format: bool + """ + When True, image cannot be deleted unless all volumes, created from it, are + deleted. + """ + + hw_firmware_type: Optional[Literal["bios", "uefi"]] + """Specifies the type of firmware with which to boot the guest.""" + + os_distro: Optional[str] + """OS Distribution, i.e. Debian, CentOS, Ubuntu, CoreOS etc.""" + + os_type: Optional[Literal["linux", "windows"]] + """The operating system installed on the image. Linux by default""" + + os_version: Optional[str] + """OS version, i.e. 19.04 (for Ubuntu) or 9.4 for Debian""" + + ssh_key: Literal["allow", "deny", "required"] + """Permission to use a ssh key in instances""" + + tags: Dict[str, str] + """Key-value tags to associate with the resource. + + A tag is a key-value pair that can be associated with a resource, enabling + efficient filtering and grouping for better organization and management. Some + tags are read-only and cannot be modified by the user. Tags are also integrated + with cost reports, allowing cost data to be filtered based on tag keys or + values. + """ diff --git a/src/gcore/types/cloud/gpu_virtual_clusters/server_delete_params.py b/src/gcore/types/cloud/gpu_virtual_clusters/server_delete_params.py new file mode 100644 index 00000000..714cb4bb --- /dev/null +++ b/src/gcore/types/cloud/gpu_virtual_clusters/server_delete_params.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["ServerDeleteParams"] + + +class ServerDeleteParams(TypedDict, total=False): + project_id: int + """Project ID""" + + region_id: int + """Region ID""" + + cluster_id: Required[str] + """Cluster unique identifier""" + + all_floating_ips: bool + """ + Flag indicating whether the floating ips associated with server / cluster are + deleted + """ + + all_reserved_fixed_ips: bool + """ + Flag indicating whether the reserved fixed ips associated with server / cluster + are deleted + """ + + all_volumes: bool + """Flag indicating whether all attached volumes are deleted""" + + floating_ip_ids: SequenceNotStr[str] + """Optional list of floating ips to be deleted""" + + reserved_fixed_ip_ids: SequenceNotStr[str] + """Optional list of reserved fixed ips to be deleted""" + + volume_ids: SequenceNotStr[str] + """Optional list of volumes to be deleted""" diff --git a/src/gcore/types/cloud/gpu_virtual_clusters/server_list_params.py b/src/gcore/types/cloud/gpu_virtual_clusters/server_list_params.py new file mode 100644 index 00000000..35c7f374 --- /dev/null +++ b/src/gcore/types/cloud/gpu_virtual_clusters/server_list_params.py @@ -0,0 +1,75 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Literal, Annotated, TypedDict + +from ...._types import SequenceNotStr +from ...._utils import PropertyInfo + +__all__ = ["ServerListParams"] + + +class ServerListParams(TypedDict, total=False): + project_id: int + """Project ID""" + + region_id: int + """Region ID""" + + changed_before: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """ + Filters the results to include only servers whose last change timestamp is less + than the specified datetime. Format: ISO 8601. + """ + + changed_since: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """ + Filters the results to include only servers whose last change timestamp is + greater than or equal to the specified datetime. Format: ISO 8601. + """ + + ip_address: str + """Filter servers by ip address.""" + + limit: int + """Limit of items on a single page""" + + name: str + """Filter servers by name. + + You can provide a full or partial name, servers with matching names will be + returned. For example, entering 'test' will return all servers that contain + 'test' in their name. + """ + + offset: int + """Offset in results list""" + + order_by: Literal["created_at.asc", "created_at.desc", "status.asc", "status.desc"] + """Order field""" + + status: Literal[ + "ACTIVE", + "BUILD", + "ERROR", + "HARD_REBOOT", + "MIGRATING", + "PAUSED", + "REBOOT", + "REBUILD", + "RESIZE", + "REVERT_RESIZE", + "SHELVED", + "SHELVED_OFFLOADED", + "SHUTOFF", + "SOFT_DELETED", + "SUSPENDED", + "VERIFY_RESIZE", + ] + """Filters servers by status.""" + + uuids: SequenceNotStr[str] + """Filter servers by uuid.""" diff --git a/tests/api_resources/cloud/gpu_virtual_clusters/__init__.py b/tests/api_resources/cloud/gpu_virtual_clusters/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/cloud/gpu_virtual_clusters/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/cloud/gpu_virtual_clusters/test_flavors.py b/tests/api_resources/cloud/gpu_virtual_clusters/test_flavors.py new file mode 100644 index 00000000..89b949ed --- /dev/null +++ b/tests/api_resources/cloud/gpu_virtual_clusters/test_flavors.py @@ -0,0 +1,112 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gcore import Gcore, AsyncGcore +from tests.utils import assert_matches_type +from gcore.types.cloud.gpu_virtual_clusters import GPUVirtualFlavorList + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestFlavors: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_list(self, client: Gcore) -> None: + flavor = client.cloud.gpu_virtual_clusters.flavors.list( + project_id=1, + region_id=7, + ) + assert_matches_type(GPUVirtualFlavorList, flavor, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Gcore) -> None: + flavor = client.cloud.gpu_virtual_clusters.flavors.list( + project_id=1, + region_id=7, + hide_disabled=True, + include_prices=True, + ) + assert_matches_type(GPUVirtualFlavorList, flavor, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.flavors.with_raw_response.list( + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + flavor = response.parse() + assert_matches_type(GPUVirtualFlavorList, flavor, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.flavors.with_streaming_response.list( + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + flavor = response.parse() + assert_matches_type(GPUVirtualFlavorList, flavor, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncFlavors: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_list(self, async_client: AsyncGcore) -> None: + flavor = await async_client.cloud.gpu_virtual_clusters.flavors.list( + project_id=1, + region_id=7, + ) + assert_matches_type(GPUVirtualFlavorList, flavor, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGcore) -> None: + flavor = await async_client.cloud.gpu_virtual_clusters.flavors.list( + project_id=1, + region_id=7, + hide_disabled=True, + include_prices=True, + ) + assert_matches_type(GPUVirtualFlavorList, flavor, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.flavors.with_raw_response.list( + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + flavor = await response.parse() + assert_matches_type(GPUVirtualFlavorList, flavor, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.flavors.with_streaming_response.list( + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + flavor = await response.parse() + assert_matches_type(GPUVirtualFlavorList, flavor, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/cloud/gpu_virtual_clusters/test_images.py b/tests/api_resources/cloud/gpu_virtual_clusters/test_images.py new file mode 100644 index 00000000..7312ccb7 --- /dev/null +++ b/tests/api_resources/cloud/gpu_virtual_clusters/test_images.py @@ -0,0 +1,392 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gcore import Gcore, AsyncGcore +from tests.utils import assert_matches_type +from gcore.types.cloud import GPUImage, TaskIDList, GPUImageList + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestImages: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_list(self, client: Gcore) -> None: + image = client.cloud.gpu_virtual_clusters.images.list( + project_id=1, + region_id=7, + ) + assert_matches_type(GPUImageList, image, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.images.with_raw_response.list( + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = response.parse() + assert_matches_type(GPUImageList, image, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.images.with_streaming_response.list( + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = response.parse() + assert_matches_type(GPUImageList, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: Gcore) -> None: + image = client.cloud.gpu_virtual_clusters.images.delete( + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, + ) + assert_matches_type(TaskIDList, image, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.images.with_raw_response.delete( + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = response.parse() + assert_matches_type(TaskIDList, image, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.images.with_streaming_response.delete( + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = response.parse() + assert_matches_type(TaskIDList, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: Gcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `image_id` but received ''"): + client.cloud.gpu_virtual_clusters.images.with_raw_response.delete( + image_id="", + project_id=1, + region_id=7, + ) + + @parametrize + def test_method_get(self, client: Gcore) -> None: + image = client.cloud.gpu_virtual_clusters.images.get( + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, + ) + assert_matches_type(GPUImage, image, path=["response"]) + + @parametrize + def test_raw_response_get(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.images.with_raw_response.get( + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = response.parse() + assert_matches_type(GPUImage, image, path=["response"]) + + @parametrize + def test_streaming_response_get(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.images.with_streaming_response.get( + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = response.parse() + assert_matches_type(GPUImage, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_get(self, client: Gcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `image_id` but received ''"): + client.cloud.gpu_virtual_clusters.images.with_raw_response.get( + image_id="", + project_id=1, + region_id=7, + ) + + @parametrize + def test_method_upload(self, client: Gcore) -> None: + image = client.cloud.gpu_virtual_clusters.images.upload( + project_id=1, + region_id=7, + name="ubuntu-23.10-x64", + url="http://mirror.noris.net/cirros/0.4.0/cirros-0.4.0-x86_64-disk.img", + ) + assert_matches_type(TaskIDList, image, path=["response"]) + + @parametrize + def test_method_upload_with_all_params(self, client: Gcore) -> None: + image = client.cloud.gpu_virtual_clusters.images.upload( + project_id=1, + region_id=7, + name="ubuntu-23.10-x64", + url="http://mirror.noris.net/cirros/0.4.0/cirros-0.4.0-x86_64-disk.img", + architecture="x86_64", + cow_format=True, + hw_firmware_type="bios", + os_distro="os_distro", + os_type="linux", + os_version="19.04", + ssh_key="allow", + tags={"my-tag": "my-tag-value"}, + ) + assert_matches_type(TaskIDList, image, path=["response"]) + + @parametrize + def test_raw_response_upload(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.images.with_raw_response.upload( + project_id=1, + region_id=7, + name="ubuntu-23.10-x64", + url="http://mirror.noris.net/cirros/0.4.0/cirros-0.4.0-x86_64-disk.img", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = response.parse() + assert_matches_type(TaskIDList, image, path=["response"]) + + @parametrize + def test_streaming_response_upload(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.images.with_streaming_response.upload( + project_id=1, + region_id=7, + name="ubuntu-23.10-x64", + url="http://mirror.noris.net/cirros/0.4.0/cirros-0.4.0-x86_64-disk.img", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = response.parse() + assert_matches_type(TaskIDList, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncImages: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_list(self, async_client: AsyncGcore) -> None: + image = await async_client.cloud.gpu_virtual_clusters.images.list( + project_id=1, + region_id=7, + ) + assert_matches_type(GPUImageList, image, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.images.with_raw_response.list( + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = await response.parse() + assert_matches_type(GPUImageList, image, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.images.with_streaming_response.list( + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = await response.parse() + assert_matches_type(GPUImageList, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncGcore) -> None: + image = await async_client.cloud.gpu_virtual_clusters.images.delete( + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, + ) + assert_matches_type(TaskIDList, image, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.images.with_raw_response.delete( + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = await response.parse() + assert_matches_type(TaskIDList, image, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.images.with_streaming_response.delete( + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = await response.parse() + assert_matches_type(TaskIDList, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncGcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `image_id` but received ''"): + await async_client.cloud.gpu_virtual_clusters.images.with_raw_response.delete( + image_id="", + project_id=1, + region_id=7, + ) + + @parametrize + async def test_method_get(self, async_client: AsyncGcore) -> None: + image = await async_client.cloud.gpu_virtual_clusters.images.get( + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, + ) + assert_matches_type(GPUImage, image, path=["response"]) + + @parametrize + async def test_raw_response_get(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.images.with_raw_response.get( + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = await response.parse() + assert_matches_type(GPUImage, image, path=["response"]) + + @parametrize + async def test_streaming_response_get(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.images.with_streaming_response.get( + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = await response.parse() + assert_matches_type(GPUImage, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_get(self, async_client: AsyncGcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `image_id` but received ''"): + await async_client.cloud.gpu_virtual_clusters.images.with_raw_response.get( + image_id="", + project_id=1, + region_id=7, + ) + + @parametrize + async def test_method_upload(self, async_client: AsyncGcore) -> None: + image = await async_client.cloud.gpu_virtual_clusters.images.upload( + project_id=1, + region_id=7, + name="ubuntu-23.10-x64", + url="http://mirror.noris.net/cirros/0.4.0/cirros-0.4.0-x86_64-disk.img", + ) + assert_matches_type(TaskIDList, image, path=["response"]) + + @parametrize + async def test_method_upload_with_all_params(self, async_client: AsyncGcore) -> None: + image = await async_client.cloud.gpu_virtual_clusters.images.upload( + project_id=1, + region_id=7, + name="ubuntu-23.10-x64", + url="http://mirror.noris.net/cirros/0.4.0/cirros-0.4.0-x86_64-disk.img", + architecture="x86_64", + cow_format=True, + hw_firmware_type="bios", + os_distro="os_distro", + os_type="linux", + os_version="19.04", + ssh_key="allow", + tags={"my-tag": "my-tag-value"}, + ) + assert_matches_type(TaskIDList, image, path=["response"]) + + @parametrize + async def test_raw_response_upload(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.images.with_raw_response.upload( + project_id=1, + region_id=7, + name="ubuntu-23.10-x64", + url="http://mirror.noris.net/cirros/0.4.0/cirros-0.4.0-x86_64-disk.img", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = await response.parse() + assert_matches_type(TaskIDList, image, path=["response"]) + + @parametrize + async def test_streaming_response_upload(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.images.with_streaming_response.upload( + project_id=1, + region_id=7, + name="ubuntu-23.10-x64", + url="http://mirror.noris.net/cirros/0.4.0/cirros-0.4.0-x86_64-disk.img", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = await response.parse() + assert_matches_type(TaskIDList, image, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/cloud/gpu_virtual_clusters/test_interfaces.py b/tests/api_resources/cloud/gpu_virtual_clusters/test_interfaces.py new file mode 100644 index 00000000..c36ac28b --- /dev/null +++ b/tests/api_resources/cloud/gpu_virtual_clusters/test_interfaces.py @@ -0,0 +1,116 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gcore import Gcore, AsyncGcore +from tests.utils import assert_matches_type +from gcore.types.cloud.gpu_virtual_clusters import GPUVirtualInterfaceList + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestInterfaces: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_list(self, client: Gcore) -> None: + interface = client.cloud.gpu_virtual_clusters.interfaces.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + assert_matches_type(GPUVirtualInterfaceList, interface, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.interfaces.with_raw_response.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + interface = response.parse() + assert_matches_type(GPUVirtualInterfaceList, interface, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.interfaces.with_streaming_response.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + interface = response.parse() + assert_matches_type(GPUVirtualInterfaceList, interface, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: Gcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + client.cloud.gpu_virtual_clusters.interfaces.with_raw_response.list( + cluster_id="", + project_id=1, + region_id=7, + ) + + +class TestAsyncInterfaces: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_list(self, async_client: AsyncGcore) -> None: + interface = await async_client.cloud.gpu_virtual_clusters.interfaces.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + assert_matches_type(GPUVirtualInterfaceList, interface, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.interfaces.with_raw_response.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + interface = await response.parse() + assert_matches_type(GPUVirtualInterfaceList, interface, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.interfaces.with_streaming_response.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + interface = await response.parse() + assert_matches_type(GPUVirtualInterfaceList, interface, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncGcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + await async_client.cloud.gpu_virtual_clusters.interfaces.with_raw_response.list( + cluster_id="", + project_id=1, + region_id=7, + ) diff --git a/tests/api_resources/cloud/gpu_virtual_clusters/test_servers.py b/tests/api_resources/cloud/gpu_virtual_clusters/test_servers.py new file mode 100644 index 00000000..6e341c99 --- /dev/null +++ b/tests/api_resources/cloud/gpu_virtual_clusters/test_servers.py @@ -0,0 +1,302 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gcore import Gcore, AsyncGcore +from tests.utils import assert_matches_type +from gcore._utils import parse_datetime +from gcore.types.cloud import TaskIDList +from gcore.types.cloud.gpu_virtual_clusters import GPUVirtualClusterServerList + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestServers: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_list(self, client: Gcore) -> None: + server = client.cloud.gpu_virtual_clusters.servers.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + assert_matches_type(GPUVirtualClusterServerList, server, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Gcore) -> None: + server = client.cloud.gpu_virtual_clusters.servers.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + changed_before=parse_datetime("2025-10-01T12:00:00Z"), + changed_since=parse_datetime("2025-10-01T12:00:00Z"), + ip_address="237.84.2.178", + limit=10, + name="name", + offset=0, + order_by="created_at.asc", + status="ACTIVE", + uuids=["string"], + ) + assert_matches_type(GPUVirtualClusterServerList, server, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.servers.with_raw_response.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + server = response.parse() + assert_matches_type(GPUVirtualClusterServerList, server, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.servers.with_streaming_response.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + server = response.parse() + assert_matches_type(GPUVirtualClusterServerList, server, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: Gcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + client.cloud.gpu_virtual_clusters.servers.with_raw_response.list( + cluster_id="", + project_id=1, + region_id=7, + ) + + @parametrize + def test_method_delete(self, client: Gcore) -> None: + server = client.cloud.gpu_virtual_clusters.servers.delete( + server_id="f1c1eeb6-1834-48c9-a7b0-daafce64872b", + project_id=1, + region_id=7, + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + ) + assert_matches_type(TaskIDList, server, path=["response"]) + + @parametrize + def test_method_delete_with_all_params(self, client: Gcore) -> None: + server = client.cloud.gpu_virtual_clusters.servers.delete( + server_id="f1c1eeb6-1834-48c9-a7b0-daafce64872b", + project_id=1, + region_id=7, + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + all_floating_ips=True, + all_reserved_fixed_ips=True, + all_volumes=True, + floating_ip_ids=["e4a01208-d6ac-4304-bf86-3028154b070a"], + reserved_fixed_ip_ids=["a29b8e1e-08d3-4cec-91fb-06e81e5f46d5"], + volume_ids=["1333c684-c3da-4b91-ac9e-a92706aa2824"], + ) + assert_matches_type(TaskIDList, server, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.servers.with_raw_response.delete( + server_id="f1c1eeb6-1834-48c9-a7b0-daafce64872b", + project_id=1, + region_id=7, + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + server = response.parse() + assert_matches_type(TaskIDList, server, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.servers.with_streaming_response.delete( + server_id="f1c1eeb6-1834-48c9-a7b0-daafce64872b", + project_id=1, + region_id=7, + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + server = response.parse() + assert_matches_type(TaskIDList, server, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: Gcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + client.cloud.gpu_virtual_clusters.servers.with_raw_response.delete( + server_id="f1c1eeb6-1834-48c9-a7b0-daafce64872b", + project_id=1, + region_id=7, + cluster_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `server_id` but received ''"): + client.cloud.gpu_virtual_clusters.servers.with_raw_response.delete( + server_id="", + project_id=1, + region_id=7, + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + ) + + +class TestAsyncServers: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_list(self, async_client: AsyncGcore) -> None: + server = await async_client.cloud.gpu_virtual_clusters.servers.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + assert_matches_type(GPUVirtualClusterServerList, server, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGcore) -> None: + server = await async_client.cloud.gpu_virtual_clusters.servers.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + changed_before=parse_datetime("2025-10-01T12:00:00Z"), + changed_since=parse_datetime("2025-10-01T12:00:00Z"), + ip_address="237.84.2.178", + limit=10, + name="name", + offset=0, + order_by="created_at.asc", + status="ACTIVE", + uuids=["string"], + ) + assert_matches_type(GPUVirtualClusterServerList, server, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.servers.with_raw_response.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + server = await response.parse() + assert_matches_type(GPUVirtualClusterServerList, server, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.servers.with_streaming_response.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + server = await response.parse() + assert_matches_type(GPUVirtualClusterServerList, server, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncGcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + await async_client.cloud.gpu_virtual_clusters.servers.with_raw_response.list( + cluster_id="", + project_id=1, + region_id=7, + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncGcore) -> None: + server = await async_client.cloud.gpu_virtual_clusters.servers.delete( + server_id="f1c1eeb6-1834-48c9-a7b0-daafce64872b", + project_id=1, + region_id=7, + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + ) + assert_matches_type(TaskIDList, server, path=["response"]) + + @parametrize + async def test_method_delete_with_all_params(self, async_client: AsyncGcore) -> None: + server = await async_client.cloud.gpu_virtual_clusters.servers.delete( + server_id="f1c1eeb6-1834-48c9-a7b0-daafce64872b", + project_id=1, + region_id=7, + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + all_floating_ips=True, + all_reserved_fixed_ips=True, + all_volumes=True, + floating_ip_ids=["e4a01208-d6ac-4304-bf86-3028154b070a"], + reserved_fixed_ip_ids=["a29b8e1e-08d3-4cec-91fb-06e81e5f46d5"], + volume_ids=["1333c684-c3da-4b91-ac9e-a92706aa2824"], + ) + assert_matches_type(TaskIDList, server, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.servers.with_raw_response.delete( + server_id="f1c1eeb6-1834-48c9-a7b0-daafce64872b", + project_id=1, + region_id=7, + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + server = await response.parse() + assert_matches_type(TaskIDList, server, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.servers.with_streaming_response.delete( + server_id="f1c1eeb6-1834-48c9-a7b0-daafce64872b", + project_id=1, + region_id=7, + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + server = await response.parse() + assert_matches_type(TaskIDList, server, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncGcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + await async_client.cloud.gpu_virtual_clusters.servers.with_raw_response.delete( + server_id="f1c1eeb6-1834-48c9-a7b0-daafce64872b", + project_id=1, + region_id=7, + cluster_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `server_id` but received ''"): + await async_client.cloud.gpu_virtual_clusters.servers.with_raw_response.delete( + server_id="", + project_id=1, + region_id=7, + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + ) diff --git a/tests/api_resources/cloud/gpu_virtual_clusters/test_volumes.py b/tests/api_resources/cloud/gpu_virtual_clusters/test_volumes.py new file mode 100644 index 00000000..294f4466 --- /dev/null +++ b/tests/api_resources/cloud/gpu_virtual_clusters/test_volumes.py @@ -0,0 +1,116 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gcore import Gcore, AsyncGcore +from tests.utils import assert_matches_type +from gcore.types.cloud.gpu_virtual_clusters import GPUVirtualClusterVolumeList + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestVolumes: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_list(self, client: Gcore) -> None: + volume = client.cloud.gpu_virtual_clusters.volumes.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + assert_matches_type(GPUVirtualClusterVolumeList, volume, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.volumes.with_raw_response.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + volume = response.parse() + assert_matches_type(GPUVirtualClusterVolumeList, volume, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.volumes.with_streaming_response.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + volume = response.parse() + assert_matches_type(GPUVirtualClusterVolumeList, volume, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: Gcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + client.cloud.gpu_virtual_clusters.volumes.with_raw_response.list( + cluster_id="", + project_id=1, + region_id=7, + ) + + +class TestAsyncVolumes: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_list(self, async_client: AsyncGcore) -> None: + volume = await async_client.cloud.gpu_virtual_clusters.volumes.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + assert_matches_type(GPUVirtualClusterVolumeList, volume, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.volumes.with_raw_response.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + volume = await response.parse() + assert_matches_type(GPUVirtualClusterVolumeList, volume, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.volumes.with_streaming_response.list( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + volume = await response.parse() + assert_matches_type(GPUVirtualClusterVolumeList, volume, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncGcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + await async_client.cloud.gpu_virtual_clusters.volumes.with_raw_response.list( + cluster_id="", + project_id=1, + region_id=7, + ) diff --git a/tests/api_resources/cloud/test_gpu_virtual_clusters.py b/tests/api_resources/cloud/test_gpu_virtual_clusters.py new file mode 100644 index 00000000..6f3758e5 --- /dev/null +++ b/tests/api_resources/cloud/test_gpu_virtual_clusters.py @@ -0,0 +1,1294 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gcore import Gcore, AsyncGcore +from tests.utils import assert_matches_type +from gcore.pagination import SyncOffsetPage, AsyncOffsetPage +from gcore.types.cloud import ( + TaskIDList, + GPUVirtualCluster, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestGPUVirtualClusters: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Gcore) -> None: + gpu_virtual_cluster = client.cloud.gpu_virtual_clusters.create( + project_id=1, + region_id=7, + flavor="g3-ai-32-192-1500-l40s-48-1", + name="gpu-cluster-1", + servers_count=3, + servers_settings={ + "interfaces": [{"type": "external"}], + "volumes": [ + { + "boot_index": 1, + "name": "my-data-disk", + "size": 100, + "source": "new", + "type": "cold", + } + ], + }, + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Gcore) -> None: + gpu_virtual_cluster = client.cloud.gpu_virtual_clusters.create( + project_id=1, + region_id=7, + flavor="g3-ai-32-192-1500-l40s-48-1", + name="gpu-cluster-1", + servers_count=3, + servers_settings={ + "interfaces": [ + { + "type": "external", + "ip_family": "ipv4", + "name": "eth0", + } + ], + "volumes": [ + { + "boot_index": 1, + "name": "my-data-disk", + "size": 100, + "source": "new", + "type": "cold", + "delete_on_termination": True, + "tags": {"key1": "value1"}, + } + ], + "credentials": { + "password": "securepassword", + "ssh_key_name": "my-ssh-key", + "username": "admin", + }, + "file_shares": [ + { + "id": "a3f2d1b8-45e6-4f8a-bb5d-19dbf2cd7e9a", + "mount_path": "/mnt/vast", + } + ], + "security_groups": [{"id": "b4849ffa-89f2-45a1-951f-0ae5b7809d98"}], + "user_data": "eyJ0ZXN0IjogImRhdGEifQ==", + }, + tags={"my-tag": "my-tag-value"}, + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.with_raw_response.create( + project_id=1, + region_id=7, + flavor="g3-ai-32-192-1500-l40s-48-1", + name="gpu-cluster-1", + servers_count=3, + servers_settings={ + "interfaces": [{"type": "external"}], + "volumes": [ + { + "boot_index": 1, + "name": "my-data-disk", + "size": 100, + "source": "new", + "type": "cold", + } + ], + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.with_streaming_response.create( + project_id=1, + region_id=7, + flavor="g3-ai-32-192-1500-l40s-48-1", + name="gpu-cluster-1", + servers_count=3, + servers_settings={ + "interfaces": [{"type": "external"}], + "volumes": [ + { + "boot_index": 1, + "name": "my-data-disk", + "size": 100, + "source": "new", + "type": "cold", + } + ], + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_update(self, client: Gcore) -> None: + gpu_virtual_cluster = client.cloud.gpu_virtual_clusters.update( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + name="gpu-cluster-1", + ) + assert_matches_type(GPUVirtualCluster, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.with_raw_response.update( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + name="gpu-cluster-1", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = response.parse() + assert_matches_type(GPUVirtualCluster, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.with_streaming_response.update( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + name="gpu-cluster-1", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = response.parse() + assert_matches_type(GPUVirtualCluster, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: Gcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + client.cloud.gpu_virtual_clusters.with_raw_response.update( + cluster_id="", + project_id=1, + region_id=7, + name="gpu-cluster-1", + ) + + @parametrize + def test_method_list(self, client: Gcore) -> None: + gpu_virtual_cluster = client.cloud.gpu_virtual_clusters.list( + project_id=1, + region_id=7, + ) + assert_matches_type(SyncOffsetPage[GPUVirtualCluster], gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Gcore) -> None: + gpu_virtual_cluster = client.cloud.gpu_virtual_clusters.list( + project_id=1, + region_id=7, + limit=10, + offset=0, + ) + assert_matches_type(SyncOffsetPage[GPUVirtualCluster], gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.with_raw_response.list( + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = response.parse() + assert_matches_type(SyncOffsetPage[GPUVirtualCluster], gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.with_streaming_response.list( + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = response.parse() + assert_matches_type(SyncOffsetPage[GPUVirtualCluster], gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: Gcore) -> None: + gpu_virtual_cluster = client.cloud.gpu_virtual_clusters.delete( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_method_delete_with_all_params(self, client: Gcore) -> None: + gpu_virtual_cluster = client.cloud.gpu_virtual_clusters.delete( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + all_floating_ips=True, + all_reserved_fixed_ips=True, + all_volumes=True, + floating_ip_ids=["e4a01208-d6ac-4304-bf86-3028154b070a"], + reserved_fixed_ip_ids=["a29b8e1e-08d3-4cec-91fb-06e81e5f46d5"], + volume_ids=["1333c684-c3da-4b91-ac9e-a92706aa2824"], + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.with_raw_response.delete( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.with_streaming_response.delete( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: Gcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + client.cloud.gpu_virtual_clusters.with_raw_response.delete( + cluster_id="", + project_id=1, + region_id=7, + ) + + @parametrize + def test_method_action_overload_1(self, client: Gcore) -> None: + gpu_virtual_cluster = client.cloud.gpu_virtual_clusters.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="start", + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_raw_response_action_overload_1(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="start", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_streaming_response_action_overload_1(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.with_streaming_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="start", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_action_overload_1(self, client: Gcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="", + project_id=1, + region_id=7, + action="start", + ) + + @parametrize + def test_method_action_overload_2(self, client: Gcore) -> None: + gpu_virtual_cluster = client.cloud.gpu_virtual_clusters.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="stop", + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_raw_response_action_overload_2(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="stop", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_streaming_response_action_overload_2(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.with_streaming_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="stop", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_action_overload_2(self, client: Gcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="", + project_id=1, + region_id=7, + action="stop", + ) + + @parametrize + def test_method_action_overload_3(self, client: Gcore) -> None: + gpu_virtual_cluster = client.cloud.gpu_virtual_clusters.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="soft_reboot", + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_raw_response_action_overload_3(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="soft_reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_streaming_response_action_overload_3(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.with_streaming_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="soft_reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_action_overload_3(self, client: Gcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="", + project_id=1, + region_id=7, + action="soft_reboot", + ) + + @parametrize + def test_method_action_overload_4(self, client: Gcore) -> None: + gpu_virtual_cluster = client.cloud.gpu_virtual_clusters.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="hard_reboot", + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_raw_response_action_overload_4(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="hard_reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_streaming_response_action_overload_4(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.with_streaming_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="hard_reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_action_overload_4(self, client: Gcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="", + project_id=1, + region_id=7, + action="hard_reboot", + ) + + @parametrize + def test_method_action_overload_5(self, client: Gcore) -> None: + gpu_virtual_cluster = client.cloud.gpu_virtual_clusters.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="update_tags", + tags={"foo": "my-tag-value"}, + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_raw_response_action_overload_5(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="update_tags", + tags={"foo": "my-tag-value"}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_streaming_response_action_overload_5(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.with_streaming_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="update_tags", + tags={"foo": "my-tag-value"}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_action_overload_5(self, client: Gcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="", + project_id=1, + region_id=7, + action="update_tags", + tags={"foo": "my-tag-value"}, + ) + + @parametrize + def test_method_action_overload_6(self, client: Gcore) -> None: + gpu_virtual_cluster = client.cloud.gpu_virtual_clusters.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="resize", + servers_count=5, + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_raw_response_action_overload_6(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="resize", + servers_count=5, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_streaming_response_action_overload_6(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.with_streaming_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="resize", + servers_count=5, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_action_overload_6(self, client: Gcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="", + project_id=1, + region_id=7, + action="resize", + servers_count=5, + ) + + @parametrize + def test_method_get(self, client: Gcore) -> None: + gpu_virtual_cluster = client.cloud.gpu_virtual_clusters.get( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + assert_matches_type(GPUVirtualCluster, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_raw_response_get(self, client: Gcore) -> None: + response = client.cloud.gpu_virtual_clusters.with_raw_response.get( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = response.parse() + assert_matches_type(GPUVirtualCluster, gpu_virtual_cluster, path=["response"]) + + @parametrize + def test_streaming_response_get(self, client: Gcore) -> None: + with client.cloud.gpu_virtual_clusters.with_streaming_response.get( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = response.parse() + assert_matches_type(GPUVirtualCluster, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_get(self, client: Gcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + client.cloud.gpu_virtual_clusters.with_raw_response.get( + cluster_id="", + project_id=1, + region_id=7, + ) + + +class TestAsyncGPUVirtualClusters: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncGcore) -> None: + gpu_virtual_cluster = await async_client.cloud.gpu_virtual_clusters.create( + project_id=1, + region_id=7, + flavor="g3-ai-32-192-1500-l40s-48-1", + name="gpu-cluster-1", + servers_count=3, + servers_settings={ + "interfaces": [{"type": "external"}], + "volumes": [ + { + "boot_index": 1, + "name": "my-data-disk", + "size": 100, + "source": "new", + "type": "cold", + } + ], + }, + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> None: + gpu_virtual_cluster = await async_client.cloud.gpu_virtual_clusters.create( + project_id=1, + region_id=7, + flavor="g3-ai-32-192-1500-l40s-48-1", + name="gpu-cluster-1", + servers_count=3, + servers_settings={ + "interfaces": [ + { + "type": "external", + "ip_family": "ipv4", + "name": "eth0", + } + ], + "volumes": [ + { + "boot_index": 1, + "name": "my-data-disk", + "size": 100, + "source": "new", + "type": "cold", + "delete_on_termination": True, + "tags": {"key1": "value1"}, + } + ], + "credentials": { + "password": "securepassword", + "ssh_key_name": "my-ssh-key", + "username": "admin", + }, + "file_shares": [ + { + "id": "a3f2d1b8-45e6-4f8a-bb5d-19dbf2cd7e9a", + "mount_path": "/mnt/vast", + } + ], + "security_groups": [{"id": "b4849ffa-89f2-45a1-951f-0ae5b7809d98"}], + "user_data": "eyJ0ZXN0IjogImRhdGEifQ==", + }, + tags={"my-tag": "my-tag-value"}, + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.with_raw_response.create( + project_id=1, + region_id=7, + flavor="g3-ai-32-192-1500-l40s-48-1", + name="gpu-cluster-1", + servers_count=3, + servers_settings={ + "interfaces": [{"type": "external"}], + "volumes": [ + { + "boot_index": 1, + "name": "my-data-disk", + "size": 100, + "source": "new", + "type": "cold", + } + ], + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = await response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.with_streaming_response.create( + project_id=1, + region_id=7, + flavor="g3-ai-32-192-1500-l40s-48-1", + name="gpu-cluster-1", + servers_count=3, + servers_settings={ + "interfaces": [{"type": "external"}], + "volumes": [ + { + "boot_index": 1, + "name": "my-data-disk", + "size": 100, + "source": "new", + "type": "cold", + } + ], + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = await response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_update(self, async_client: AsyncGcore) -> None: + gpu_virtual_cluster = await async_client.cloud.gpu_virtual_clusters.update( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + name="gpu-cluster-1", + ) + assert_matches_type(GPUVirtualCluster, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.with_raw_response.update( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + name="gpu-cluster-1", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = await response.parse() + assert_matches_type(GPUVirtualCluster, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.with_streaming_response.update( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + name="gpu-cluster-1", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = await response.parse() + assert_matches_type(GPUVirtualCluster, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncGcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + await async_client.cloud.gpu_virtual_clusters.with_raw_response.update( + cluster_id="", + project_id=1, + region_id=7, + name="gpu-cluster-1", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncGcore) -> None: + gpu_virtual_cluster = await async_client.cloud.gpu_virtual_clusters.list( + project_id=1, + region_id=7, + ) + assert_matches_type(AsyncOffsetPage[GPUVirtualCluster], gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGcore) -> None: + gpu_virtual_cluster = await async_client.cloud.gpu_virtual_clusters.list( + project_id=1, + region_id=7, + limit=10, + offset=0, + ) + assert_matches_type(AsyncOffsetPage[GPUVirtualCluster], gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.with_raw_response.list( + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = await response.parse() + assert_matches_type(AsyncOffsetPage[GPUVirtualCluster], gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.with_streaming_response.list( + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = await response.parse() + assert_matches_type(AsyncOffsetPage[GPUVirtualCluster], gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncGcore) -> None: + gpu_virtual_cluster = await async_client.cloud.gpu_virtual_clusters.delete( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_method_delete_with_all_params(self, async_client: AsyncGcore) -> None: + gpu_virtual_cluster = await async_client.cloud.gpu_virtual_clusters.delete( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + all_floating_ips=True, + all_reserved_fixed_ips=True, + all_volumes=True, + floating_ip_ids=["e4a01208-d6ac-4304-bf86-3028154b070a"], + reserved_fixed_ip_ids=["a29b8e1e-08d3-4cec-91fb-06e81e5f46d5"], + volume_ids=["1333c684-c3da-4b91-ac9e-a92706aa2824"], + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.with_raw_response.delete( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = await response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.with_streaming_response.delete( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = await response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncGcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + await async_client.cloud.gpu_virtual_clusters.with_raw_response.delete( + cluster_id="", + project_id=1, + region_id=7, + ) + + @parametrize + async def test_method_action_overload_1(self, async_client: AsyncGcore) -> None: + gpu_virtual_cluster = await async_client.cloud.gpu_virtual_clusters.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="start", + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_raw_response_action_overload_1(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="start", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = await response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_streaming_response_action_overload_1(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.with_streaming_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="start", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = await response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_action_overload_1(self, async_client: AsyncGcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + await async_client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="", + project_id=1, + region_id=7, + action="start", + ) + + @parametrize + async def test_method_action_overload_2(self, async_client: AsyncGcore) -> None: + gpu_virtual_cluster = await async_client.cloud.gpu_virtual_clusters.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="stop", + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_raw_response_action_overload_2(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="stop", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = await response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_streaming_response_action_overload_2(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.with_streaming_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="stop", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = await response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_action_overload_2(self, async_client: AsyncGcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + await async_client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="", + project_id=1, + region_id=7, + action="stop", + ) + + @parametrize + async def test_method_action_overload_3(self, async_client: AsyncGcore) -> None: + gpu_virtual_cluster = await async_client.cloud.gpu_virtual_clusters.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="soft_reboot", + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_raw_response_action_overload_3(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="soft_reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = await response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_streaming_response_action_overload_3(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.with_streaming_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="soft_reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = await response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_action_overload_3(self, async_client: AsyncGcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + await async_client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="", + project_id=1, + region_id=7, + action="soft_reboot", + ) + + @parametrize + async def test_method_action_overload_4(self, async_client: AsyncGcore) -> None: + gpu_virtual_cluster = await async_client.cloud.gpu_virtual_clusters.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="hard_reboot", + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_raw_response_action_overload_4(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="hard_reboot", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = await response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_streaming_response_action_overload_4(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.with_streaming_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="hard_reboot", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = await response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_action_overload_4(self, async_client: AsyncGcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + await async_client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="", + project_id=1, + region_id=7, + action="hard_reboot", + ) + + @parametrize + async def test_method_action_overload_5(self, async_client: AsyncGcore) -> None: + gpu_virtual_cluster = await async_client.cloud.gpu_virtual_clusters.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="update_tags", + tags={"foo": "my-tag-value"}, + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_raw_response_action_overload_5(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="update_tags", + tags={"foo": "my-tag-value"}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = await response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_streaming_response_action_overload_5(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.with_streaming_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="update_tags", + tags={"foo": "my-tag-value"}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = await response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_action_overload_5(self, async_client: AsyncGcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + await async_client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="", + project_id=1, + region_id=7, + action="update_tags", + tags={"foo": "my-tag-value"}, + ) + + @parametrize + async def test_method_action_overload_6(self, async_client: AsyncGcore) -> None: + gpu_virtual_cluster = await async_client.cloud.gpu_virtual_clusters.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="resize", + servers_count=5, + ) + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_raw_response_action_overload_6(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="resize", + servers_count=5, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = await response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_streaming_response_action_overload_6(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.with_streaming_response.action( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + action="resize", + servers_count=5, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = await response.parse() + assert_matches_type(TaskIDList, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_action_overload_6(self, async_client: AsyncGcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + await async_client.cloud.gpu_virtual_clusters.with_raw_response.action( + cluster_id="", + project_id=1, + region_id=7, + action="resize", + servers_count=5, + ) + + @parametrize + async def test_method_get(self, async_client: AsyncGcore) -> None: + gpu_virtual_cluster = await async_client.cloud.gpu_virtual_clusters.get( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + assert_matches_type(GPUVirtualCluster, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_raw_response_get(self, async_client: AsyncGcore) -> None: + response = await async_client.cloud.gpu_virtual_clusters.with_raw_response.get( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + gpu_virtual_cluster = await response.parse() + assert_matches_type(GPUVirtualCluster, gpu_virtual_cluster, path=["response"]) + + @parametrize + async def test_streaming_response_get(self, async_client: AsyncGcore) -> None: + async with async_client.cloud.gpu_virtual_clusters.with_streaming_response.get( + cluster_id="1aaaab48-10d0-46d9-80cc-85209284ceb4", + project_id=1, + region_id=7, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + gpu_virtual_cluster = await response.parse() + assert_matches_type(GPUVirtualCluster, gpu_virtual_cluster, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_get(self, async_client: AsyncGcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `cluster_id` but received ''"): + await async_client.cloud.gpu_virtual_clusters.with_raw_response.get( + cluster_id="", + project_id=1, + region_id=7, + ) From 70e1cc8404668e008c236881c5819eeff91725d6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 14:36:33 +0000 Subject: [PATCH 5/6] fix(compat): update signatures of `model_dump` and `model_dump_json` for Pydantic v1 --- src/gcore/_models.py | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/gcore/_models.py b/src/gcore/_models.py index fcec2cf9..ca9500b2 100644 --- a/src/gcore/_models.py +++ b/src/gcore/_models.py @@ -257,15 +257,16 @@ def model_dump( mode: Literal["json", "python"] | str = "python", include: IncEx | None = None, exclude: IncEx | None = None, + context: Any | None = None, by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, + exclude_computed_fields: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, - context: dict[str, Any] | None = None, - serialize_as_any: bool = False, fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, ) -> dict[str, Any]: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump @@ -273,16 +274,24 @@ def model_dump( Args: mode: The mode in which `to_python` should run. - If mode is 'json', the dictionary will only contain JSON serializable types. - If mode is 'python', the dictionary may contain any Python objects. - include: A list of fields to include in the output. - exclude: A list of fields to exclude from the output. + If mode is 'json', the output will only contain JSON serializable types. + If mode is 'python', the output may contain non-JSON-serializable Python objects. + include: A set of fields to include in the output. + exclude: A set of fields to exclude from the output. + context: Additional context to pass to the serializer. by_alias: Whether to use the field's alias in the dictionary key if defined. - exclude_unset: Whether to exclude fields that are unset or None from the output. - exclude_defaults: Whether to exclude fields that are set to their default value from the output. - exclude_none: Whether to exclude fields that have a value of `None` from the output. - round_trip: Whether to enable serialization and deserialization round-trip support. - warnings: Whether to log warnings when invalid fields are encountered. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value. + exclude_none: Whether to exclude fields that have a value of `None`. + exclude_computed_fields: Whether to exclude computed fields. + While this can be useful for round-tripping, it is usually recommended to use the dedicated + `round_trip` parameter instead. + round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T]. + warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, + "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. + fallback: A function to call when an unknown value is encountered. If not provided, + a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. Returns: A dictionary representation of the model. @@ -299,6 +308,8 @@ def model_dump( raise ValueError("serialize_as_any is only supported in Pydantic v2") if fallback is not None: raise ValueError("fallback is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") dumped = super().dict( # pyright: ignore[reportDeprecated] include=include, exclude=exclude, @@ -315,15 +326,17 @@ def model_dump_json( self, *, indent: int | None = None, + ensure_ascii: bool = False, include: IncEx | None = None, exclude: IncEx | None = None, + context: Any | None = None, by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, + exclude_computed_fields: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, - context: dict[str, Any] | None = None, fallback: Callable[[Any], Any] | None = None, serialize_as_any: bool = False, ) -> str: @@ -355,6 +368,10 @@ def model_dump_json( raise ValueError("serialize_as_any is only supported in Pydantic v2") if fallback is not None: raise ValueError("fallback is only supported in Pydantic v2") + if ensure_ascii != False: + raise ValueError("ensure_ascii is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") return super().json( # type: ignore[reportDeprecated] indent=indent, include=include, From 252e0871bba968b0b7abe66b0d86c4dfa1e14f72 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 14:37:16 +0000 Subject: [PATCH 6/6] release: 0.20.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 20 ++++++++++++++++++++ pyproject.toml | 2 +- src/gcore/_version.py | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e7562934..0c2ecec6 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.19.0" + ".": "0.20.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 658b8393..c8ad9677 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## 0.20.0 (2025-11-11) + +Full Changelog: [v0.19.0...v0.20.0](https://github.com/G-Core/gcore-python/compare/v0.19.0...v0.20.0) + +### Features + +* **api:** aggregated API specs update ([767fdd5](https://github.com/G-Core/gcore-python/commit/767fdd5d74846369e3331efba210aff12a43e802)) +* **cloud:** add support for GPU virtual clusters ([c406d97](https://github.com/G-Core/gcore-python/commit/c406d97d04db03f5c4d105f2e9761e9b8c2d96c8)) + + +### Bug Fixes + +* compat with Python 3.14 ([90bfffe](https://github.com/G-Core/gcore-python/commit/90bfffeed8b28a360e1489162d4dc6a28745b411)) +* **compat:** update signatures of `model_dump` and `model_dump_json` for Pydantic v1 ([70e1cc8](https://github.com/G-Core/gcore-python/commit/70e1cc8404668e008c236881c5819eeff91725d6)) + + +### Chores + +* **package:** drop Python 3.8 support ([99955b9](https://github.com/G-Core/gcore-python/commit/99955b9208b2810fcbafbf5f8d941b5981faebbe)) + ## 0.19.0 (2025-11-07) Full Changelog: [v0.18.0...v0.19.0](https://github.com/G-Core/gcore-python/compare/v0.18.0...v0.19.0) diff --git a/pyproject.toml b/pyproject.toml index 6aeac16c..6b4e68cb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "gcore" -version = "0.19.0" +version = "0.20.0" description = "The official Python library for the gcore API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/gcore/_version.py b/src/gcore/_version.py index 54b67f1e..0013e79a 100644 --- a/src/gcore/_version.py +++ b/src/gcore/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "gcore" -__version__ = "0.19.0" # x-release-please-version +__version__ = "0.20.0" # x-release-please-version