datacrunch 1.10.0__tar.gz → 1.11.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {datacrunch-1.10.0 → datacrunch-1.11.0}/PKG-INFO +1 -1
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/InferenceClient/inference_client.py +36 -0
- datacrunch-1.11.0/datacrunch/__version__.py +1 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/constants.py +1 -0
- datacrunch-1.11.0/datacrunch/instances/instances.py +238 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch.egg-info/PKG-INFO +1 -1
- datacrunch-1.10.0/datacrunch/__version__.py +0 -1
- datacrunch-1.10.0/datacrunch/instances/instances.py +0 -498
- {datacrunch-1.10.0 → datacrunch-1.11.0}/LICENSE +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/README.md +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/InferenceClient/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/authentication/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/authentication/authentication.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/balance/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/balance/balance.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/containers/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/containers/containers.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/datacrunch.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/exceptions.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/helpers.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/http_client/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/http_client/http_client.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/images/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/images/images.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/instance_types/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/instance_types/instance_types.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/instances/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/locations/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/locations/locations.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/ssh_keys/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/ssh_keys/ssh_keys.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/startup_scripts/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/startup_scripts/startup_scripts.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/volume_types/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/volume_types/volume_types.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/volumes/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/volumes/volumes.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch.egg-info/SOURCES.txt +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch.egg-info/dependency_links.txt +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch.egg-info/requires.txt +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch.egg-info/top_level.txt +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/setup.cfg +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/setup.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/integration_tests/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/integration_tests/conftest.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/integration_tests/test_instances.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/integration_tests/test_locations.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/integration_tests/test_volumes.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/authentication/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/authentication/test_authentication.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/balance/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/balance/test_balance.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/conftest.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/containers/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/containers/test_containers.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/http_client/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/http_client/test_http_client.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/images/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/images/test_images.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/instance_types/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/instance_types/test_instance_types.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/instances/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/instances/test_instances.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/ssh_keys/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/ssh_keys/test_ssh_keys.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/startup_scripts/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/startup_scripts/test_startup_scripts.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/test_datacrunch.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/test_exceptions.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/volume_types/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/volume_types/test_volume_types.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/volumes/__init__.py +0 -0
- {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/volumes/test_volumes.py +0 -0
|
@@ -6,16 +6,19 @@ from typing import Optional, Dict, Any, Union, Generator
|
|
|
6
6
|
from urllib.parse import urlparse
|
|
7
7
|
from enum import Enum
|
|
8
8
|
|
|
9
|
+
|
|
9
10
|
class InferenceClientError(Exception):
|
|
10
11
|
"""Base exception for InferenceClient errors."""
|
|
11
12
|
pass
|
|
12
13
|
|
|
14
|
+
|
|
13
15
|
class AsyncStatus(int, Enum):
|
|
14
16
|
Initialized = 0
|
|
15
17
|
Queue = 1
|
|
16
18
|
Inference = 2
|
|
17
19
|
Completed = 3
|
|
18
20
|
|
|
21
|
+
|
|
19
22
|
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
20
23
|
@dataclass
|
|
21
24
|
class InferenceResponse:
|
|
@@ -222,6 +225,22 @@ class InferenceClient:
|
|
|
222
225
|
raise InferenceClientError(f"Request to {path} failed: {str(e)}")
|
|
223
226
|
|
|
224
227
|
def run_sync(self, data: Dict[str, Any], path: str = "", timeout_seconds: int = 60 * 5, headers: Optional[Dict[str, str]] = None, http_method: str = "POST", stream: bool = False):
|
|
228
|
+
"""Make a synchronous request to the inference endpoint.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
data: The data payload to send with the request
|
|
232
|
+
path: API endpoint path. Defaults to empty string.
|
|
233
|
+
timeout_seconds: Request timeout in seconds. Defaults to 5 minutes.
|
|
234
|
+
headers: Optional headers to include in the request
|
|
235
|
+
http_method: HTTP method to use. Defaults to "POST".
|
|
236
|
+
stream: Whether to stream the response. Defaults to False.
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
InferenceResponse: Object containing the response data.
|
|
240
|
+
|
|
241
|
+
Raises:
|
|
242
|
+
InferenceClientError: If the request fails
|
|
243
|
+
"""
|
|
225
244
|
response = self._make_request(
|
|
226
245
|
http_method, path, json=data, timeout_seconds=timeout_seconds, headers=headers, stream=stream)
|
|
227
246
|
|
|
@@ -233,6 +252,23 @@ class InferenceClient:
|
|
|
233
252
|
)
|
|
234
253
|
|
|
235
254
|
def run(self, data: Dict[str, Any], path: str = "", timeout_seconds: int = 60 * 5, headers: Optional[Dict[str, str]] = None, http_method: str = "POST", no_response: bool = False):
|
|
255
|
+
"""Make an asynchronous request to the inference endpoint.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
data: The data payload to send with the request
|
|
259
|
+
path: API endpoint path. Defaults to empty string.
|
|
260
|
+
timeout_seconds: Request timeout in seconds. Defaults to 5 minutes.
|
|
261
|
+
headers: Optional headers to include in the request
|
|
262
|
+
http_method: HTTP method to use. Defaults to "POST".
|
|
263
|
+
no_response: If True, don't wait for response. Defaults to False.
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
AsyncInferenceExecution: Object to track the async execution status.
|
|
267
|
+
If no_response is True, returns None.
|
|
268
|
+
|
|
269
|
+
Raises:
|
|
270
|
+
InferenceClientError: If the request fails
|
|
271
|
+
"""
|
|
236
272
|
# Add relevant headers to the request, to indicate that the request is async
|
|
237
273
|
headers = headers or {}
|
|
238
274
|
if no_response:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VERSION = '1.11.0'
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from typing import List, Union, Optional, Dict, Literal
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from dataclasses_json import dataclass_json
|
|
5
|
+
from datacrunch.constants import Locations, InstanceStatus
|
|
6
|
+
|
|
7
|
+
INSTANCES_ENDPOINT = '/instances'
|
|
8
|
+
|
|
9
|
+
Contract = Literal['LONG_TERM', 'PAY_AS_YOU_GO', 'SPOT']
|
|
10
|
+
Pricing = Literal['DYNAMIC_PRICE', 'FIXED_PRICE']
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass_json
|
|
14
|
+
@dataclass
|
|
15
|
+
class Instance:
|
|
16
|
+
"""Represents a cloud instance with its configuration and state.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
id: Unique identifier for the instance.
|
|
20
|
+
instance_type: Type of the instance (e.g., '8V100.48V').
|
|
21
|
+
price_per_hour: Cost per hour of running the instance.
|
|
22
|
+
hostname: Network hostname of the instance.
|
|
23
|
+
description: Human-readable description of the instance.
|
|
24
|
+
status: Current operational status of the instance.
|
|
25
|
+
created_at: Timestamp of instance creation.
|
|
26
|
+
ssh_key_ids: List of SSH key IDs associated with the instance.
|
|
27
|
+
cpu: CPU configuration details.
|
|
28
|
+
gpu: GPU configuration details.
|
|
29
|
+
memory: Memory configuration details.
|
|
30
|
+
storage: Storage configuration details.
|
|
31
|
+
gpu_memory: GPU memory configuration details.
|
|
32
|
+
ip: IP address assigned to the instance.
|
|
33
|
+
os_volume_id: ID of the operating system volume.
|
|
34
|
+
location: Datacenter location code (default: Locations.FIN_01).
|
|
35
|
+
image: Image ID or type used for the instance.
|
|
36
|
+
startup_script_id: ID of the startup script to run.
|
|
37
|
+
is_spot: Whether the instance is a spot instance.
|
|
38
|
+
contract: Contract type for the instance. (e.g. 'LONG_TERM', 'PAY_AS_YOU_GO', 'SPOT')
|
|
39
|
+
pricing: Pricing model for the instance. (e.g. 'DYNAMIC_PRICE', 'FIXED_PRICE')
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
id: str
|
|
43
|
+
instance_type: str
|
|
44
|
+
price_per_hour: float
|
|
45
|
+
hostname: str
|
|
46
|
+
description: str
|
|
47
|
+
status: str
|
|
48
|
+
created_at: str
|
|
49
|
+
ssh_key_ids: List[str]
|
|
50
|
+
cpu: dict
|
|
51
|
+
gpu: dict
|
|
52
|
+
memory: dict
|
|
53
|
+
storage: dict
|
|
54
|
+
gpu_memory: dict
|
|
55
|
+
# Can be None if instance is still not provisioned
|
|
56
|
+
ip: Optional[str] = None
|
|
57
|
+
# Can be None if instance is still not provisioned
|
|
58
|
+
os_volume_id: Optional[str] = None
|
|
59
|
+
location: str = Locations.FIN_01
|
|
60
|
+
image: Optional[str] = None
|
|
61
|
+
startup_script_id: Optional[str] = None
|
|
62
|
+
is_spot: bool = False
|
|
63
|
+
contract: Optional[Contract] = None
|
|
64
|
+
pricing: Optional[Pricing] = None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class InstancesService:
|
|
68
|
+
"""Service for managing cloud instances through the API.
|
|
69
|
+
|
|
70
|
+
This service provides methods to create, retrieve, and manage cloud instances
|
|
71
|
+
through the DataCrunch API.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def __init__(self, http_client) -> None:
|
|
75
|
+
"""Initializes the InstancesService with an HTTP client.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
http_client: HTTP client for making API requests.
|
|
79
|
+
"""
|
|
80
|
+
self._http_client = http_client
|
|
81
|
+
|
|
82
|
+
def get(self, status: Optional[str] = None) -> List[Instance]:
|
|
83
|
+
"""Retrieves all non-deleted instances or instances with specific status.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
status: Optional status filter for instances. If None, returns all
|
|
87
|
+
non-deleted instances.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
List of instance objects matching the criteria.
|
|
91
|
+
"""
|
|
92
|
+
instances_dict = self._http_client.get(
|
|
93
|
+
INSTANCES_ENDPOINT, params={'status': status}).json()
|
|
94
|
+
return [Instance.from_dict(instance_dict, infer_missing=True) for instance_dict in instances_dict]
|
|
95
|
+
|
|
96
|
+
def get_by_id(self, id: str) -> Instance:
|
|
97
|
+
"""Retrieves a specific instance by its ID.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
id: Unique identifier of the instance to retrieve.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Instance object with the specified ID.
|
|
104
|
+
|
|
105
|
+
Raises:
|
|
106
|
+
HTTPError: If the instance is not found or other API error occurs.
|
|
107
|
+
"""
|
|
108
|
+
instance_dict = self._http_client.get(
|
|
109
|
+
INSTANCES_ENDPOINT + f'/{id}').json()
|
|
110
|
+
return Instance.from_dict(instance_dict, infer_missing=True)
|
|
111
|
+
|
|
112
|
+
def create(self,
|
|
113
|
+
instance_type: str,
|
|
114
|
+
image: str,
|
|
115
|
+
hostname: str,
|
|
116
|
+
description: str,
|
|
117
|
+
ssh_key_ids: list = [],
|
|
118
|
+
location: str = Locations.FIN_01,
|
|
119
|
+
startup_script_id: Optional[str] = None,
|
|
120
|
+
volumes: Optional[List[Dict]] = None,
|
|
121
|
+
existing_volumes: Optional[List[str]] = None,
|
|
122
|
+
os_volume: Optional[Dict] = None,
|
|
123
|
+
is_spot: bool = False,
|
|
124
|
+
contract: Optional[Contract] = None,
|
|
125
|
+
pricing: Optional[Pricing] = None,
|
|
126
|
+
coupon: Optional[str] = None) -> Instance:
|
|
127
|
+
"""Creates and deploys a new cloud instance.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
instance_type: Type of instance to create (e.g., '8V100.48V').
|
|
131
|
+
image: Image type or existing OS volume ID for the instance.
|
|
132
|
+
hostname: Network hostname for the instance.
|
|
133
|
+
description: Human-readable description of the instance.
|
|
134
|
+
ssh_key_ids: List of SSH key IDs to associate with the instance.
|
|
135
|
+
location: Datacenter location code (default: Locations.FIN_01).
|
|
136
|
+
startup_script_id: Optional ID of startup script to run.
|
|
137
|
+
volumes: Optional list of volume configurations to create.
|
|
138
|
+
existing_volumes: Optional list of existing volume IDs to attach.
|
|
139
|
+
os_volume: Optional OS volume configuration details.
|
|
140
|
+
is_spot: Whether to create a spot instance.
|
|
141
|
+
contract: Optional contract type for the instance.
|
|
142
|
+
pricing: Optional pricing model for the instance.
|
|
143
|
+
coupon: Optional coupon code for discounts.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
The newly created instance object.
|
|
147
|
+
|
|
148
|
+
Raises:
|
|
149
|
+
HTTPError: If instance creation fails or other API error occurs.
|
|
150
|
+
"""
|
|
151
|
+
payload = {
|
|
152
|
+
"instance_type": instance_type,
|
|
153
|
+
"image": image,
|
|
154
|
+
"ssh_key_ids": ssh_key_ids,
|
|
155
|
+
"startup_script_id": startup_script_id,
|
|
156
|
+
"hostname": hostname,
|
|
157
|
+
"description": description,
|
|
158
|
+
"location_code": location,
|
|
159
|
+
"os_volume": os_volume,
|
|
160
|
+
"volumes": volumes,
|
|
161
|
+
"existing_volumes": existing_volumes,
|
|
162
|
+
"is_spot": is_spot,
|
|
163
|
+
"coupon": coupon,
|
|
164
|
+
}
|
|
165
|
+
if contract:
|
|
166
|
+
payload['contract'] = contract
|
|
167
|
+
if pricing:
|
|
168
|
+
payload['pricing'] = pricing
|
|
169
|
+
id = self._http_client.post(INSTANCES_ENDPOINT, json=payload).text
|
|
170
|
+
|
|
171
|
+
# Wait for instance to enter provisioning state with timeout
|
|
172
|
+
MAX_WAIT_TIME = 60 # Maximum wait time in seconds
|
|
173
|
+
POLL_INTERVAL = 0.5 # Time between status checks
|
|
174
|
+
|
|
175
|
+
start_time = time.time()
|
|
176
|
+
while True:
|
|
177
|
+
instance = self.get_by_id(id)
|
|
178
|
+
if instance.status != InstanceStatus.ORDERED:
|
|
179
|
+
return instance
|
|
180
|
+
|
|
181
|
+
if time.time() - start_time > MAX_WAIT_TIME:
|
|
182
|
+
raise TimeoutError(
|
|
183
|
+
f"Instance {id} did not enter provisioning state within {MAX_WAIT_TIME} seconds")
|
|
184
|
+
|
|
185
|
+
time.sleep(POLL_INTERVAL)
|
|
186
|
+
|
|
187
|
+
def action(self, id_list: Union[List[str], str], action: str, volume_ids: Optional[List[str]] = None) -> None:
|
|
188
|
+
"""Performs an action on one or more instances.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
id_list: Single instance ID or list of instance IDs to act upon.
|
|
192
|
+
action: Action to perform on the instances.
|
|
193
|
+
volume_ids: Optional list of volume IDs to delete.
|
|
194
|
+
|
|
195
|
+
Raises:
|
|
196
|
+
HTTPError: If the action fails or other API error occurs.
|
|
197
|
+
"""
|
|
198
|
+
if type(id_list) is str:
|
|
199
|
+
id_list = [id_list]
|
|
200
|
+
|
|
201
|
+
payload = {
|
|
202
|
+
"id": id_list,
|
|
203
|
+
"action": action,
|
|
204
|
+
"volume_ids": volume_ids
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
self._http_client.put(INSTANCES_ENDPOINT, json=payload)
|
|
208
|
+
return
|
|
209
|
+
|
|
210
|
+
def is_available(self, instance_type: str, is_spot: bool = False, location_code: Optional[str] = None) -> bool:
|
|
211
|
+
"""Checks if a specific instance type is available for deployment.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
instance_type: Type of instance to check availability for.
|
|
215
|
+
is_spot: Whether to check spot instance availability.
|
|
216
|
+
location_code: Optional datacenter location code.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
True if the instance type is available, False otherwise.
|
|
220
|
+
"""
|
|
221
|
+
is_spot = str(is_spot).lower()
|
|
222
|
+
query_params = {'isSpot': is_spot, 'location_code': location_code}
|
|
223
|
+
url = f'/instance-availability/{instance_type}'
|
|
224
|
+
return self._http_client.get(url, query_params).json()
|
|
225
|
+
|
|
226
|
+
def get_availabilities(self, is_spot: Optional[bool] = None, location_code: Optional[str] = None) -> List[Dict]:
|
|
227
|
+
"""Retrieves a list of available instance types across locations.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
is_spot: Optional flag to filter spot instance availability.
|
|
231
|
+
location_code: Optional datacenter location code to filter by.
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
List of available instance types and their details.
|
|
235
|
+
"""
|
|
236
|
+
is_spot = str(is_spot).lower() if is_spot is not None else None
|
|
237
|
+
query_params = {'isSpot': is_spot, 'locationCode': location_code}
|
|
238
|
+
return self._http_client.get('/instance-availability', params=query_params).json()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
VERSION = '1.10.0'
|
|
@@ -1,498 +0,0 @@
|
|
|
1
|
-
from typing import List, Union, Optional, Dict, Literal
|
|
2
|
-
from datacrunch.helpers import stringify_class_object_properties
|
|
3
|
-
from datacrunch.constants import Locations
|
|
4
|
-
|
|
5
|
-
INSTANCES_ENDPOINT = '/instances'
|
|
6
|
-
|
|
7
|
-
Contract = Literal['LONG_TERM', 'PAY_AS_YOU_GO', 'SPOT']
|
|
8
|
-
Pricing = Literal['DYNAMIC_PRICE', 'FIXED_PRICE']
|
|
9
|
-
|
|
10
|
-
class Instance:
|
|
11
|
-
"""An instance model class"""
|
|
12
|
-
|
|
13
|
-
def __init__(self,
|
|
14
|
-
id: str,
|
|
15
|
-
instance_type: str,
|
|
16
|
-
image: str,
|
|
17
|
-
price_per_hour: float,
|
|
18
|
-
hostname: str,
|
|
19
|
-
description: str,
|
|
20
|
-
ip: str,
|
|
21
|
-
status: str,
|
|
22
|
-
created_at: str,
|
|
23
|
-
ssh_key_ids: List[str],
|
|
24
|
-
cpu: dict,
|
|
25
|
-
gpu: dict,
|
|
26
|
-
memory: dict,
|
|
27
|
-
storage: dict,
|
|
28
|
-
os_volume_id: str,
|
|
29
|
-
gpu_memory: dict,
|
|
30
|
-
location: str = Locations.FIN_01,
|
|
31
|
-
startup_script_id: str = None,
|
|
32
|
-
is_spot: bool = False,
|
|
33
|
-
contract: Contract = None,
|
|
34
|
-
pricing: Pricing = None,
|
|
35
|
-
) -> None:
|
|
36
|
-
"""Initialize the instance object
|
|
37
|
-
|
|
38
|
-
:param id: instance id
|
|
39
|
-
:type id: str
|
|
40
|
-
:param instance_type: instance type. e.g. '8V100.48M'
|
|
41
|
-
:type instance_type: str
|
|
42
|
-
:param image: instance image type. e.g. 'ubuntu-20.04-cuda-11.0'
|
|
43
|
-
:type image: str
|
|
44
|
-
:param price_per_hour: price per hour
|
|
45
|
-
:type price_per_hour: float
|
|
46
|
-
:param hostname: instance hostname
|
|
47
|
-
:type hostname: str
|
|
48
|
-
:param description: instance description
|
|
49
|
-
:type description: str
|
|
50
|
-
:param ip: instance ip address
|
|
51
|
-
:type ip: str
|
|
52
|
-
:param status: instance current status, might be out of date if changed
|
|
53
|
-
:type status: str
|
|
54
|
-
:param created_at: the time the instance was deployed (UTC)
|
|
55
|
-
:type created_at: str
|
|
56
|
-
:param ssh_key_ids: list of ssh keys ids
|
|
57
|
-
:type ssh_key_ids: List[str]
|
|
58
|
-
:param cpu: cpu details
|
|
59
|
-
:type cpu: dict
|
|
60
|
-
:param gpu: gpu details
|
|
61
|
-
:type gpu: dict
|
|
62
|
-
:param memory: memory details
|
|
63
|
-
:type memory: dict
|
|
64
|
-
:param storage: storate details
|
|
65
|
-
:type storage: dict
|
|
66
|
-
:param id: main OS volume id
|
|
67
|
-
:type id: str
|
|
68
|
-
:param memory: gpu memory details
|
|
69
|
-
:type memory: dict
|
|
70
|
-
:param location: datacenter location, defaults to "FIN-01"
|
|
71
|
-
:type location: str, optional
|
|
72
|
-
:param startup_script_id: startup script id, defaults to None
|
|
73
|
-
:type startup_script_id: str, optional
|
|
74
|
-
:param is_spot: is this a spot instance, defaults to None
|
|
75
|
-
:type is_spot: bool, optional
|
|
76
|
-
"""
|
|
77
|
-
self._id = id
|
|
78
|
-
self._instance_type = instance_type
|
|
79
|
-
self._image = image
|
|
80
|
-
self._price_per_hour = price_per_hour
|
|
81
|
-
self._location = location
|
|
82
|
-
self._hostname = hostname
|
|
83
|
-
self._description = description
|
|
84
|
-
self._ip = ip
|
|
85
|
-
self._status = status
|
|
86
|
-
self._created_at = created_at
|
|
87
|
-
self._ssh_key_ids = ssh_key_ids
|
|
88
|
-
self._startup_script_id = startup_script_id
|
|
89
|
-
self._cpu = cpu
|
|
90
|
-
self._gpu = gpu
|
|
91
|
-
self._memory = memory
|
|
92
|
-
self._storage = storage
|
|
93
|
-
self._os_volume_id = os_volume_id
|
|
94
|
-
self._gpu_memory = gpu_memory
|
|
95
|
-
self._is_spot = is_spot
|
|
96
|
-
self._contract = contract
|
|
97
|
-
self._pricing = pricing
|
|
98
|
-
|
|
99
|
-
@property
|
|
100
|
-
def id(self) -> str:
|
|
101
|
-
"""Get the instance id
|
|
102
|
-
|
|
103
|
-
:return: instance id
|
|
104
|
-
:rtype: str
|
|
105
|
-
"""
|
|
106
|
-
return self._id
|
|
107
|
-
|
|
108
|
-
@property
|
|
109
|
-
def instance_type(self) -> str:
|
|
110
|
-
"""Get the instance type
|
|
111
|
-
|
|
112
|
-
:return: instance type
|
|
113
|
-
:rtype: str
|
|
114
|
-
"""
|
|
115
|
-
return self._instance_type
|
|
116
|
-
|
|
117
|
-
@property
|
|
118
|
-
def image(self) -> str:
|
|
119
|
-
"""Get the instance image type
|
|
120
|
-
|
|
121
|
-
:return: instance image type
|
|
122
|
-
:rtype: str
|
|
123
|
-
"""
|
|
124
|
-
return self._image
|
|
125
|
-
|
|
126
|
-
@property
|
|
127
|
-
def price_per_hour(self) -> float:
|
|
128
|
-
"""Get the instance price per hour
|
|
129
|
-
|
|
130
|
-
:return: price per hour
|
|
131
|
-
:rtype: float
|
|
132
|
-
"""
|
|
133
|
-
return self._price_per_hour
|
|
134
|
-
|
|
135
|
-
@property
|
|
136
|
-
def location(self) -> str:
|
|
137
|
-
"""Get the instance datacenter location
|
|
138
|
-
|
|
139
|
-
:return: datacenter location
|
|
140
|
-
:rtype: str
|
|
141
|
-
"""
|
|
142
|
-
return self._location
|
|
143
|
-
|
|
144
|
-
@property
|
|
145
|
-
def hostname(self) -> str:
|
|
146
|
-
"""Get the instance hostname
|
|
147
|
-
|
|
148
|
-
:return: hostname
|
|
149
|
-
:rtype: str
|
|
150
|
-
"""
|
|
151
|
-
return self._hostname
|
|
152
|
-
|
|
153
|
-
@property
|
|
154
|
-
def description(self) -> str:
|
|
155
|
-
"""Get the instance description
|
|
156
|
-
|
|
157
|
-
:return: instance description
|
|
158
|
-
:rtype: str
|
|
159
|
-
"""
|
|
160
|
-
return self._description
|
|
161
|
-
|
|
162
|
-
@property
|
|
163
|
-
def ip(self) -> str:
|
|
164
|
-
"""Get the instance ip address
|
|
165
|
-
|
|
166
|
-
:return: ip address
|
|
167
|
-
:rtype: str
|
|
168
|
-
"""
|
|
169
|
-
return self._ip
|
|
170
|
-
|
|
171
|
-
@property
|
|
172
|
-
def status(self) -> str:
|
|
173
|
-
"""Get the current instance status. might be out of date if changed.
|
|
174
|
-
|
|
175
|
-
:return: instance status
|
|
176
|
-
:rtype: str
|
|
177
|
-
"""
|
|
178
|
-
return self._status
|
|
179
|
-
|
|
180
|
-
@property
|
|
181
|
-
def created_at(self) -> str:
|
|
182
|
-
"""Get the time when the instance was deployed (UTC)
|
|
183
|
-
|
|
184
|
-
:return: time
|
|
185
|
-
:rtype: str
|
|
186
|
-
"""
|
|
187
|
-
return self._created_at
|
|
188
|
-
|
|
189
|
-
@property
|
|
190
|
-
def ssh_key_ids(self) -> List[str]:
|
|
191
|
-
"""Get the SSH key IDs of the instance
|
|
192
|
-
|
|
193
|
-
:return: SSH key IDs
|
|
194
|
-
:rtype: List[str]
|
|
195
|
-
"""
|
|
196
|
-
return self._ssh_key_ids
|
|
197
|
-
|
|
198
|
-
@property
|
|
199
|
-
def startup_script_id(self) -> Union[str, None]:
|
|
200
|
-
"""Get the startup script ID or None if the is no script
|
|
201
|
-
|
|
202
|
-
:return: startup script ID or None
|
|
203
|
-
:rtype: Union[str, None]
|
|
204
|
-
"""
|
|
205
|
-
return self._startup_script_id
|
|
206
|
-
|
|
207
|
-
@property
|
|
208
|
-
def cpu(self) -> dict:
|
|
209
|
-
"""Get the instance cpu details
|
|
210
|
-
|
|
211
|
-
:return: cpu details
|
|
212
|
-
:rtype: dict
|
|
213
|
-
"""
|
|
214
|
-
return self._cpu
|
|
215
|
-
|
|
216
|
-
@property
|
|
217
|
-
def gpu(self) -> dict:
|
|
218
|
-
"""Get the instance gpu details
|
|
219
|
-
|
|
220
|
-
:return: gpu details
|
|
221
|
-
:rtype: dict
|
|
222
|
-
"""
|
|
223
|
-
return self._gpu
|
|
224
|
-
|
|
225
|
-
@property
|
|
226
|
-
def memory(self) -> dict:
|
|
227
|
-
"""Get the instance memory details
|
|
228
|
-
|
|
229
|
-
:return: memory details
|
|
230
|
-
:rtype: dict
|
|
231
|
-
"""
|
|
232
|
-
return self._memory
|
|
233
|
-
|
|
234
|
-
@property
|
|
235
|
-
def storage(self) -> dict:
|
|
236
|
-
"""Get the instance storage details
|
|
237
|
-
|
|
238
|
-
:return: storage details
|
|
239
|
-
:rtype: dict
|
|
240
|
-
"""
|
|
241
|
-
return self._storage
|
|
242
|
-
|
|
243
|
-
@property
|
|
244
|
-
def os_volume_id(self) -> str:
|
|
245
|
-
"""Get the main os volume id
|
|
246
|
-
|
|
247
|
-
:return: main os volume id
|
|
248
|
-
:rtype: str
|
|
249
|
-
"""
|
|
250
|
-
return self._os_volume_id
|
|
251
|
-
|
|
252
|
-
@property
|
|
253
|
-
def gpu_memory(self) -> dict:
|
|
254
|
-
"""Get the instance gpu_memory details
|
|
255
|
-
|
|
256
|
-
:return: gpu_memory details
|
|
257
|
-
:rtype: dict
|
|
258
|
-
"""
|
|
259
|
-
return self._gpu_memory
|
|
260
|
-
|
|
261
|
-
@property
|
|
262
|
-
def is_spot(self) -> bool:
|
|
263
|
-
"""Is this a spot instance
|
|
264
|
-
|
|
265
|
-
:return: is spot details
|
|
266
|
-
:rtype: bool
|
|
267
|
-
"""
|
|
268
|
-
return self._is_spot
|
|
269
|
-
|
|
270
|
-
@property
|
|
271
|
-
def contract(self) -> bool:
|
|
272
|
-
"""Get contract type
|
|
273
|
-
|
|
274
|
-
:return: contract type
|
|
275
|
-
:rtype: str
|
|
276
|
-
"""
|
|
277
|
-
return self._contract
|
|
278
|
-
|
|
279
|
-
@property
|
|
280
|
-
def pricing(self) -> bool:
|
|
281
|
-
"""Get pricing type
|
|
282
|
-
|
|
283
|
-
:return: pricing type
|
|
284
|
-
:rtype: str
|
|
285
|
-
"""
|
|
286
|
-
return self._pricing
|
|
287
|
-
|
|
288
|
-
def __str__(self) -> str:
|
|
289
|
-
"""Returns a string of the json representation of the instance
|
|
290
|
-
|
|
291
|
-
:return: json representation of the instance
|
|
292
|
-
:rtype: str
|
|
293
|
-
"""
|
|
294
|
-
return stringify_class_object_properties(self)
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
class InstancesService:
|
|
298
|
-
"""A service for interacting with the instances endpoint"""
|
|
299
|
-
|
|
300
|
-
def __init__(self, http_client) -> None:
|
|
301
|
-
self._http_client = http_client
|
|
302
|
-
|
|
303
|
-
def get(self, status: str = None) -> List[Instance]:
|
|
304
|
-
"""Get all of the client's non-deleted instances, or instances with specific status.
|
|
305
|
-
|
|
306
|
-
:param status: optional, status of the instances, defaults to None
|
|
307
|
-
:type status: str, optional
|
|
308
|
-
:return: list of instance details objects
|
|
309
|
-
:rtype: List[Instance]
|
|
310
|
-
"""
|
|
311
|
-
instances_dict = self._http_client.get(
|
|
312
|
-
INSTANCES_ENDPOINT, params={'status': status}).json()
|
|
313
|
-
instances = list(map(lambda instance_dict: Instance(
|
|
314
|
-
id=instance_dict['id'],
|
|
315
|
-
instance_type=instance_dict['instance_type'],
|
|
316
|
-
image=instance_dict['image'],
|
|
317
|
-
price_per_hour=instance_dict['price_per_hour'] if 'price_per_hour' in instance_dict else None,
|
|
318
|
-
location=instance_dict['location'],
|
|
319
|
-
hostname=instance_dict['hostname'],
|
|
320
|
-
description=instance_dict['description'],
|
|
321
|
-
ip=instance_dict['ip'],
|
|
322
|
-
status=instance_dict['status'],
|
|
323
|
-
created_at=instance_dict['created_at'],
|
|
324
|
-
ssh_key_ids=instance_dict['ssh_key_ids'] if 'ssh_key_ids' in instance_dict else [
|
|
325
|
-
],
|
|
326
|
-
startup_script_id=instance_dict['startup_script_id'] if 'startup_script_id' in instance_dict else None,
|
|
327
|
-
cpu=instance_dict['cpu'],
|
|
328
|
-
gpu=instance_dict['gpu'],
|
|
329
|
-
memory=instance_dict['memory'],
|
|
330
|
-
storage=instance_dict['storage'],
|
|
331
|
-
os_volume_id=instance_dict['os_volume_id'] if 'os_volume_id' in instance_dict else None,
|
|
332
|
-
gpu_memory=instance_dict['gpu_memory'] if 'gpu_memory' in instance_dict else None,
|
|
333
|
-
is_spot=instance_dict['is_spot'] if 'is_spot' in instance_dict else False,
|
|
334
|
-
contract=instance_dict['contract'] if 'contract' in instance_dict else False,
|
|
335
|
-
pricing=instance_dict['pricing'] if 'pricing' in instance_dict else False,
|
|
336
|
-
), instances_dict))
|
|
337
|
-
return instances
|
|
338
|
-
|
|
339
|
-
def get_by_id(self, id: str) -> Instance:
|
|
340
|
-
"""Get an instance with specified id.
|
|
341
|
-
|
|
342
|
-
:param id: instance id
|
|
343
|
-
:type id: str
|
|
344
|
-
:return: instance details object
|
|
345
|
-
:rtype: Instance
|
|
346
|
-
"""
|
|
347
|
-
instance_dict = self._http_client.get(
|
|
348
|
-
INSTANCES_ENDPOINT + f'/{id}').json()
|
|
349
|
-
instance = Instance(
|
|
350
|
-
id=instance_dict['id'],
|
|
351
|
-
instance_type=instance_dict['instance_type'],
|
|
352
|
-
image=instance_dict['image'],
|
|
353
|
-
price_per_hour=instance_dict['price_per_hour'] if 'price_per_hour' in instance_dict else None,
|
|
354
|
-
location=instance_dict['location'],
|
|
355
|
-
hostname=instance_dict['hostname'],
|
|
356
|
-
description=instance_dict['description'],
|
|
357
|
-
ip=instance_dict['ip'],
|
|
358
|
-
status=instance_dict['status'],
|
|
359
|
-
created_at=instance_dict['created_at'],
|
|
360
|
-
ssh_key_ids=instance_dict['ssh_key_ids'] if 'ssh_key_ids' in instance_dict else [
|
|
361
|
-
],
|
|
362
|
-
startup_script_id=instance_dict['startup_script_id'] if 'startup_script_id' in instance_dict else None,
|
|
363
|
-
cpu=instance_dict['cpu'],
|
|
364
|
-
gpu=instance_dict['gpu'],
|
|
365
|
-
memory=instance_dict['memory'],
|
|
366
|
-
storage=instance_dict['storage'],
|
|
367
|
-
os_volume_id=instance_dict['os_volume_id'] if 'os_volume_id' in instance_dict else None,
|
|
368
|
-
gpu_memory=instance_dict['gpu_memory'] if 'gpu_memory' in instance_dict else None,
|
|
369
|
-
is_spot=instance_dict['is_spot'] if 'is_spot' in instance_dict else False,
|
|
370
|
-
contract=instance_dict['contract'] if 'contract' in instance_dict else False,
|
|
371
|
-
pricing=instance_dict['pricing'] if 'pricing' in instance_dict else False,
|
|
372
|
-
)
|
|
373
|
-
return instance
|
|
374
|
-
|
|
375
|
-
def create(self,
|
|
376
|
-
instance_type: str,
|
|
377
|
-
image: str,
|
|
378
|
-
hostname: str,
|
|
379
|
-
description: str,
|
|
380
|
-
ssh_key_ids: list = [],
|
|
381
|
-
location: str = Locations.FIN_01,
|
|
382
|
-
startup_script_id: str = None,
|
|
383
|
-
volumes: List[Dict] = None,
|
|
384
|
-
existing_volumes: List[str] = None,
|
|
385
|
-
os_volume: Dict = None,
|
|
386
|
-
is_spot: bool = False,
|
|
387
|
-
contract: Contract = None,
|
|
388
|
-
pricing: Pricing = None,
|
|
389
|
-
coupon: str = None) -> Instance:
|
|
390
|
-
"""Creates (deploys) a new instance
|
|
391
|
-
|
|
392
|
-
:param instance_type: instance type. e.g. '8V100.48M'
|
|
393
|
-
:type instance_type: str
|
|
394
|
-
:param image: instance image type. e.g. 'ubuntu-20.04-cuda-11.0', or existing OS volume id
|
|
395
|
-
:type image: str
|
|
396
|
-
:param ssh_key_ids: list of ssh key ids
|
|
397
|
-
:type ssh_key_ids: list
|
|
398
|
-
:param hostname: instance hostname
|
|
399
|
-
:type hostname: str
|
|
400
|
-
:param description: instance description
|
|
401
|
-
:type description: str
|
|
402
|
-
:param location: datacenter location, defaults to "FIN-01"
|
|
403
|
-
:type location: str, optional
|
|
404
|
-
:param startup_script_id: startup script id, defaults to None
|
|
405
|
-
:type startup_script_id: str, optional
|
|
406
|
-
:param volumes: List of volume data dictionaries to create alongside the instance
|
|
407
|
-
:type volumes: List[Dict], optional
|
|
408
|
-
:param existing_volumes: List of existing volume ids to attach to the instance
|
|
409
|
-
:type existing_volumes: List[str], optional
|
|
410
|
-
:param os_volume: OS volume details, defaults to None
|
|
411
|
-
:type os_volume: Dict, optional
|
|
412
|
-
:param is_spot: Is spot instance
|
|
413
|
-
:type is_spot: bool, optional
|
|
414
|
-
:param pricing: Pricing type
|
|
415
|
-
:type pricing: str, optional
|
|
416
|
-
:param contract: Contract type
|
|
417
|
-
:type contract: str, optional
|
|
418
|
-
:param coupon: coupon code
|
|
419
|
-
:type coupon: str, optional
|
|
420
|
-
:return: the new instance object
|
|
421
|
-
:rtype: id
|
|
422
|
-
"""
|
|
423
|
-
payload = {
|
|
424
|
-
"instance_type": instance_type,
|
|
425
|
-
"image": image,
|
|
426
|
-
"ssh_key_ids": ssh_key_ids,
|
|
427
|
-
"startup_script_id": startup_script_id,
|
|
428
|
-
"hostname": hostname,
|
|
429
|
-
"description": description,
|
|
430
|
-
"location_code": location,
|
|
431
|
-
"os_volume": os_volume,
|
|
432
|
-
"volumes": volumes,
|
|
433
|
-
"existing_volumes": existing_volumes,
|
|
434
|
-
"is_spot": is_spot,
|
|
435
|
-
"coupon": coupon,
|
|
436
|
-
}
|
|
437
|
-
if contract:
|
|
438
|
-
payload['contract'] = contract
|
|
439
|
-
if pricing:
|
|
440
|
-
payload['pricing'] = pricing
|
|
441
|
-
id = self._http_client.post(INSTANCES_ENDPOINT, json=payload).text
|
|
442
|
-
instance = self.get_by_id(id)
|
|
443
|
-
return instance
|
|
444
|
-
|
|
445
|
-
def action(self, id_list: Union[List[str], str], action: str, volume_ids: Optional[List[str]] = None) -> None:
|
|
446
|
-
"""Performs an action on a list of instances / single instance
|
|
447
|
-
|
|
448
|
-
:param id_list: list of instance ids, or an instance id
|
|
449
|
-
:type id_list: Union[List[str], str]
|
|
450
|
-
:param action: the action to perform
|
|
451
|
-
:type action: str
|
|
452
|
-
:param volume_ids: the volume ids to delete
|
|
453
|
-
:type volume_ids: Optional[List[str]]
|
|
454
|
-
"""
|
|
455
|
-
if type(id_list) is str:
|
|
456
|
-
id_list = [id_list]
|
|
457
|
-
|
|
458
|
-
payload = {
|
|
459
|
-
"id": id_list,
|
|
460
|
-
"action": action,
|
|
461
|
-
"volume_ids": volume_ids
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
self._http_client.put(INSTANCES_ENDPOINT, json=payload)
|
|
465
|
-
return
|
|
466
|
-
|
|
467
|
-
# TODO: use enum/const for location_code
|
|
468
|
-
def is_available(self, instance_type: str, is_spot: bool = False, location_code: str = None) -> bool:
|
|
469
|
-
"""Returns True if a specific instance type is now available for deployment
|
|
470
|
-
|
|
471
|
-
:param instance_type: instance type
|
|
472
|
-
:type instance_type: str
|
|
473
|
-
:param is_spot: Is spot instance
|
|
474
|
-
:type is_spot: bool, optional
|
|
475
|
-
:param location_code: datacenter location, defaults to "FIN-01"
|
|
476
|
-
:type location_code: str, optional
|
|
477
|
-
:return: True if available to deploy, False otherwise
|
|
478
|
-
:rtype: bool
|
|
479
|
-
"""
|
|
480
|
-
is_spot = str(is_spot).lower()
|
|
481
|
-
query_params = {'isSpot': is_spot, 'location_code': location_code}
|
|
482
|
-
url = f'/instance-availability/{instance_type}'
|
|
483
|
-
return self._http_client.get(url, query_params).json()
|
|
484
|
-
|
|
485
|
-
# TODO: use enum/const for location_code
|
|
486
|
-
def get_availabilities(self, is_spot: bool = None, location_code: str = None) -> bool:
|
|
487
|
-
"""Returns a list of available instance types
|
|
488
|
-
|
|
489
|
-
:param is_spot: Is spot instance
|
|
490
|
-
:type is_spot: bool, optional
|
|
491
|
-
:param location_code: datacenter location, defaults to "FIN-01"
|
|
492
|
-
:type location_code: str, optional
|
|
493
|
-
:return: list of available instance types in every location
|
|
494
|
-
:rtype: list
|
|
495
|
-
"""
|
|
496
|
-
is_spot = str(is_spot).lower() if is_spot is not None else None
|
|
497
|
-
query_params = {'isSpot': is_spot, 'locationCode': location_code}
|
|
498
|
-
return self._http_client.get('/instance-availability', params=query_params).json()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/authentication/test_authentication.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/instance_types/test_instance_types.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/startup_scripts/test_startup_scripts.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|