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.
Files changed (76) hide show
  1. {datacrunch-1.10.0 → datacrunch-1.11.0}/PKG-INFO +1 -1
  2. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/InferenceClient/inference_client.py +36 -0
  3. datacrunch-1.11.0/datacrunch/__version__.py +1 -0
  4. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/constants.py +1 -0
  5. datacrunch-1.11.0/datacrunch/instances/instances.py +238 -0
  6. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch.egg-info/PKG-INFO +1 -1
  7. datacrunch-1.10.0/datacrunch/__version__.py +0 -1
  8. datacrunch-1.10.0/datacrunch/instances/instances.py +0 -498
  9. {datacrunch-1.10.0 → datacrunch-1.11.0}/LICENSE +0 -0
  10. {datacrunch-1.10.0 → datacrunch-1.11.0}/README.md +0 -0
  11. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/InferenceClient/__init__.py +0 -0
  12. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/__init__.py +0 -0
  13. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/authentication/__init__.py +0 -0
  14. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/authentication/authentication.py +0 -0
  15. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/balance/__init__.py +0 -0
  16. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/balance/balance.py +0 -0
  17. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/containers/__init__.py +0 -0
  18. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/containers/containers.py +0 -0
  19. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/datacrunch.py +0 -0
  20. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/exceptions.py +0 -0
  21. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/helpers.py +0 -0
  22. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/http_client/__init__.py +0 -0
  23. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/http_client/http_client.py +0 -0
  24. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/images/__init__.py +0 -0
  25. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/images/images.py +0 -0
  26. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/instance_types/__init__.py +0 -0
  27. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/instance_types/instance_types.py +0 -0
  28. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/instances/__init__.py +0 -0
  29. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/locations/__init__.py +0 -0
  30. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/locations/locations.py +0 -0
  31. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/ssh_keys/__init__.py +0 -0
  32. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/ssh_keys/ssh_keys.py +0 -0
  33. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/startup_scripts/__init__.py +0 -0
  34. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/startup_scripts/startup_scripts.py +0 -0
  35. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/volume_types/__init__.py +0 -0
  36. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/volume_types/volume_types.py +0 -0
  37. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/volumes/__init__.py +0 -0
  38. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch/volumes/volumes.py +0 -0
  39. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch.egg-info/SOURCES.txt +0 -0
  40. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch.egg-info/dependency_links.txt +0 -0
  41. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch.egg-info/requires.txt +0 -0
  42. {datacrunch-1.10.0 → datacrunch-1.11.0}/datacrunch.egg-info/top_level.txt +0 -0
  43. {datacrunch-1.10.0 → datacrunch-1.11.0}/setup.cfg +0 -0
  44. {datacrunch-1.10.0 → datacrunch-1.11.0}/setup.py +0 -0
  45. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/__init__.py +0 -0
  46. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/integration_tests/__init__.py +0 -0
  47. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/integration_tests/conftest.py +0 -0
  48. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/integration_tests/test_instances.py +0 -0
  49. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/integration_tests/test_locations.py +0 -0
  50. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/integration_tests/test_volumes.py +0 -0
  51. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/__init__.py +0 -0
  52. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/authentication/__init__.py +0 -0
  53. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/authentication/test_authentication.py +0 -0
  54. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/balance/__init__.py +0 -0
  55. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/balance/test_balance.py +0 -0
  56. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/conftest.py +0 -0
  57. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/containers/__init__.py +0 -0
  58. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/containers/test_containers.py +0 -0
  59. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/http_client/__init__.py +0 -0
  60. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/http_client/test_http_client.py +0 -0
  61. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/images/__init__.py +0 -0
  62. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/images/test_images.py +0 -0
  63. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/instance_types/__init__.py +0 -0
  64. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/instance_types/test_instance_types.py +0 -0
  65. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/instances/__init__.py +0 -0
  66. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/instances/test_instances.py +0 -0
  67. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/ssh_keys/__init__.py +0 -0
  68. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/ssh_keys/test_ssh_keys.py +0 -0
  69. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/startup_scripts/__init__.py +0 -0
  70. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/startup_scripts/test_startup_scripts.py +0 -0
  71. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/test_datacrunch.py +0 -0
  72. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/test_exceptions.py +0 -0
  73. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/volume_types/__init__.py +0 -0
  74. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/volume_types/test_volume_types.py +0 -0
  75. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/volumes/__init__.py +0 -0
  76. {datacrunch-1.10.0 → datacrunch-1.11.0}/tests/unit_tests/volumes/test_volumes.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datacrunch
3
- Version: 1.10.0
3
+ Version: 1.11.0
4
4
  Summary: Official Python SDK for DataCrunch Public API
5
5
  Home-page: https://github.com/DataCrunch-io
6
6
  Author: DataCrunch Oy
@@ -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'
@@ -22,6 +22,7 @@ class VolumeActions:
22
22
 
23
23
 
24
24
  class InstanceStatus:
25
+ ORDERED = 'ordered'
25
26
  RUNNING = 'running'
26
27
  PROVISIONING = 'provisioning'
27
28
  OFFLINE = 'offline'
@@ -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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datacrunch
3
- Version: 1.10.0
3
+ Version: 1.11.0
4
4
  Summary: Official Python SDK for DataCrunch Public API
5
5
  Home-page: https://github.com/DataCrunch-io
6
6
  Author: DataCrunch Oy
@@ -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