verda 0.1.0__py3-none-any.whl → 1.17.0__py3-none-any.whl
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.
- verda/InferenceClient/__init__.py +3 -0
- verda/InferenceClient/inference_client.py +525 -0
- verda/__init__.py +22 -2
- verda/_version.py +6 -0
- verda/authentication/authentication.py +106 -0
- verda/balance/__init__.py +0 -0
- verda/balance/balance.py +50 -0
- verda/constants.py +126 -0
- verda/containers/__init__.py +33 -0
- verda/containers/containers.py +1110 -0
- verda/datacrunch.py +43 -0
- verda/exceptions.py +30 -0
- verda/helpers.py +17 -0
- verda/http_client/__init__.py +0 -0
- verda/http_client/http_client.py +246 -0
- verda/images/__init__.py +0 -0
- verda/images/images.py +88 -0
- verda/instance_types/__init__.py +0 -0
- verda/instance_types/instance_types.py +67 -0
- verda/instances/__init__.py +0 -0
- verda/instances/instances.py +260 -0
- verda/locations/__init__.py +0 -0
- verda/locations/locations.py +13 -0
- verda/ssh_keys/__init__.py +0 -0
- verda/ssh_keys/ssh_keys.py +109 -0
- verda/startup_scripts/__init__.py +0 -0
- verda/startup_scripts/startup_scripts.py +110 -0
- verda/verda.py +83 -0
- verda/volume_types/__init__.py +0 -0
- verda/volume_types/volume_types.py +66 -0
- verda/volumes/__init__.py +0 -0
- verda/volumes/volumes.py +386 -0
- verda-1.17.0.dist-info/METADATA +182 -0
- verda-1.17.0.dist-info/RECORD +36 -0
- {verda-0.1.0.dist-info → verda-1.17.0.dist-info}/WHEEL +1 -1
- verda-0.1.0.dist-info/METADATA +0 -10
- verda-0.1.0.dist-info/RECORD +0 -5
- /verda/{py.typed → authentication/__init__.py} +0 -0
verda/datacrunch.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Frozen, minimal compatibility layer for old DataCrunch API
|
|
2
|
+
|
|
3
|
+
from verda import DataCrunchClient
|
|
4
|
+
from verda._version import __version__
|
|
5
|
+
from verda.authentication.authentication import AuthenticationService
|
|
6
|
+
from verda.balance.balance import BalanceService
|
|
7
|
+
from verda.constants import Constants
|
|
8
|
+
from verda.containers.containers import ContainersService
|
|
9
|
+
from verda.http_client.http_client import HTTPClient
|
|
10
|
+
from verda.images.images import ImagesService
|
|
11
|
+
from verda.instance_types.instance_types import InstanceTypesService
|
|
12
|
+
from verda.instances.instances import InstancesService
|
|
13
|
+
from verda.locations.locations import LocationsService
|
|
14
|
+
from verda.ssh_keys.ssh_keys import SSHKeysService
|
|
15
|
+
from verda.startup_scripts.startup_scripts import StartupScriptsService
|
|
16
|
+
from verda.volume_types.volume_types import VolumeTypesService
|
|
17
|
+
from verda.volumes.volumes import VolumesService
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
'AuthenticationService',
|
|
21
|
+
'BalanceService',
|
|
22
|
+
'Constants',
|
|
23
|
+
'ContainersService',
|
|
24
|
+
'DataCrunchClient',
|
|
25
|
+
'HTTPClient',
|
|
26
|
+
'ImagesService',
|
|
27
|
+
'InstanceTypesService',
|
|
28
|
+
'InstancesService',
|
|
29
|
+
'LocationsService',
|
|
30
|
+
'SSHKeysService',
|
|
31
|
+
'StartupScriptsService',
|
|
32
|
+
'VolumeTypesService',
|
|
33
|
+
'VolumesService',
|
|
34
|
+
'__version__',
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
import warnings
|
|
38
|
+
|
|
39
|
+
warnings.warn(
|
|
40
|
+
'datacrunch.datacrunch is deprecated; use `from verda` instead.',
|
|
41
|
+
DeprecationWarning,
|
|
42
|
+
stacklevel=2,
|
|
43
|
+
)
|
verda/exceptions.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
class APIException(Exception):
|
|
2
|
+
"""This exception is raised if there was an error from verda's API.
|
|
3
|
+
|
|
4
|
+
Could be an invalid input, token etc.
|
|
5
|
+
|
|
6
|
+
Raised when an API HTTP call response has a status code >= 400
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
def __init__(self, code: str, message: str) -> None:
|
|
10
|
+
"""API Exception.
|
|
11
|
+
|
|
12
|
+
:param code: error code
|
|
13
|
+
:type code: str
|
|
14
|
+
:param message: error message
|
|
15
|
+
:type message: str
|
|
16
|
+
"""
|
|
17
|
+
self.code = code
|
|
18
|
+
"""Error code. should be available in VerdaClient.error_codes"""
|
|
19
|
+
|
|
20
|
+
self.message = message
|
|
21
|
+
"""Error message
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __str__(self) -> str:
|
|
25
|
+
msg = ''
|
|
26
|
+
if self.code:
|
|
27
|
+
msg = f'error code: {self.code}\n'
|
|
28
|
+
|
|
29
|
+
msg += f'message: {self.message}'
|
|
30
|
+
return msg
|
verda/helpers.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def stringify_class_object_properties(class_object: type) -> str:
|
|
5
|
+
"""Generates a json string representation of a class object's properties and values.
|
|
6
|
+
|
|
7
|
+
:param class_object: An instance of a class
|
|
8
|
+
:type class_object: Type
|
|
9
|
+
:return: _description_
|
|
10
|
+
:rtype: json string representation of a class object's properties and values
|
|
11
|
+
"""
|
|
12
|
+
class_properties = {
|
|
13
|
+
property: getattr(class_object, property, '')
|
|
14
|
+
for property in class_object.__dir__() # noqa: A001
|
|
15
|
+
if property[:1] != '_' and type(getattr(class_object, property, '')).__name__ != 'method'
|
|
16
|
+
}
|
|
17
|
+
return json.dumps(class_properties, indent=2)
|
|
File without changes
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
from verda._version import __version__
|
|
6
|
+
from verda.exceptions import APIException
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def handle_error(response: requests.Response) -> None:
|
|
10
|
+
"""Checks for the response status code and raises an exception if it's 400 or higher.
|
|
11
|
+
|
|
12
|
+
:param response: the API call response
|
|
13
|
+
:raises APIException: an api exception with message and error type code
|
|
14
|
+
"""
|
|
15
|
+
if not response.ok:
|
|
16
|
+
data = json.loads(response.text)
|
|
17
|
+
code = data['code'] if 'code' in data else None
|
|
18
|
+
message = data['message'] if 'message' in data else None
|
|
19
|
+
raise APIException(code, message)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class HTTPClient:
|
|
23
|
+
"""An http client, a wrapper for the requests library.
|
|
24
|
+
|
|
25
|
+
For each request, it adds the authentication header with an access token.
|
|
26
|
+
If the access token is expired it refreshes it before calling the specified API endpoint.
|
|
27
|
+
Also checks the response status code and raises an exception if needed.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, auth_service, base_url: str) -> None:
|
|
31
|
+
self._version = __version__
|
|
32
|
+
self._base_url = base_url
|
|
33
|
+
self._auth_service = auth_service
|
|
34
|
+
self._auth_service.authenticate()
|
|
35
|
+
|
|
36
|
+
def post(
|
|
37
|
+
self, url: str, json: dict | None = None, params: dict | None = None, **kwargs
|
|
38
|
+
) -> requests.Response:
|
|
39
|
+
"""Sends a POST request.
|
|
40
|
+
|
|
41
|
+
A wrapper for the requests.post method.
|
|
42
|
+
|
|
43
|
+
Builds the url, uses custom headers, refresh tokens if needed.
|
|
44
|
+
|
|
45
|
+
:param url: relative url of the API endpoint
|
|
46
|
+
:type url: str
|
|
47
|
+
:param json: A JSON serializable Python object to send in the body of the Request, defaults to None
|
|
48
|
+
:type json: dict, optional
|
|
49
|
+
:param params: Dictionary of querystring data to attach to the Request, defaults to None
|
|
50
|
+
:type params: dict, optional
|
|
51
|
+
|
|
52
|
+
:raises APIException: an api exception with message and error type code
|
|
53
|
+
|
|
54
|
+
:return: Response object
|
|
55
|
+
:rtype: requests.Response
|
|
56
|
+
"""
|
|
57
|
+
self._refresh_token_if_expired()
|
|
58
|
+
|
|
59
|
+
url = self._add_base_url(url)
|
|
60
|
+
headers = self._generate_headers()
|
|
61
|
+
|
|
62
|
+
response = requests.post(url, json=json, headers=headers, params=params, **kwargs)
|
|
63
|
+
handle_error(response)
|
|
64
|
+
|
|
65
|
+
return response
|
|
66
|
+
|
|
67
|
+
def put(
|
|
68
|
+
self, url: str, json: dict | None = None, params: dict | None = None, **kwargs
|
|
69
|
+
) -> requests.Response:
|
|
70
|
+
"""Sends a PUT request.
|
|
71
|
+
|
|
72
|
+
A wrapper for the requests.put method.
|
|
73
|
+
|
|
74
|
+
Builds the url, uses custom headers, refresh tokens if needed.
|
|
75
|
+
|
|
76
|
+
:param url: relative url of the API endpoint
|
|
77
|
+
:type url: str
|
|
78
|
+
:param json: A JSON serializable Python object to send in the body of the Request, defaults to None
|
|
79
|
+
:type json: dict, optional
|
|
80
|
+
:param params: Dictionary of querystring data to attach to the Request, defaults to None
|
|
81
|
+
:type params: dict, optional
|
|
82
|
+
|
|
83
|
+
:raises APIException: an api exception with message and error type code
|
|
84
|
+
|
|
85
|
+
:return: Response object
|
|
86
|
+
:rtype: requests.Response
|
|
87
|
+
"""
|
|
88
|
+
self._refresh_token_if_expired()
|
|
89
|
+
|
|
90
|
+
url = self._add_base_url(url)
|
|
91
|
+
headers = self._generate_headers()
|
|
92
|
+
|
|
93
|
+
response = requests.put(url, json=json, headers=headers, params=params, **kwargs)
|
|
94
|
+
handle_error(response)
|
|
95
|
+
|
|
96
|
+
return response
|
|
97
|
+
|
|
98
|
+
def get(self, url: str, params: dict | None = None, **kwargs) -> requests.Response:
|
|
99
|
+
"""Sends a GET request.
|
|
100
|
+
|
|
101
|
+
A wrapper for the requests.get method.
|
|
102
|
+
|
|
103
|
+
Builds the url, uses custom headers, refresh tokens if needed.
|
|
104
|
+
|
|
105
|
+
:param url: relative url of the API endpoint
|
|
106
|
+
:type url: str
|
|
107
|
+
:param params: Dictionary of querystring data to attach to the Request, defaults to None
|
|
108
|
+
:type params: dict, optional
|
|
109
|
+
|
|
110
|
+
:raises APIException: an api exception with message and error type code
|
|
111
|
+
|
|
112
|
+
:return: Response object
|
|
113
|
+
:rtype: requests.Response
|
|
114
|
+
"""
|
|
115
|
+
self._refresh_token_if_expired()
|
|
116
|
+
|
|
117
|
+
url = self._add_base_url(url)
|
|
118
|
+
headers = self._generate_headers()
|
|
119
|
+
|
|
120
|
+
response = requests.get(url, params=params, headers=headers, **kwargs)
|
|
121
|
+
handle_error(response)
|
|
122
|
+
|
|
123
|
+
return response
|
|
124
|
+
|
|
125
|
+
def patch(
|
|
126
|
+
self, url: str, json: dict | None = None, params: dict | None = None, **kwargs
|
|
127
|
+
) -> requests.Response:
|
|
128
|
+
"""Sends a PATCH request.
|
|
129
|
+
|
|
130
|
+
A wrapper for the requests.patch method.
|
|
131
|
+
|
|
132
|
+
Builds the url, uses custom headers, refresh tokens if needed.
|
|
133
|
+
|
|
134
|
+
:param url: relative url of the API endpoint
|
|
135
|
+
:type url: str
|
|
136
|
+
:param json: A JSON serializable Python object to send in the body of the Request, defaults to None
|
|
137
|
+
:type json: dict, optional
|
|
138
|
+
:param params: Dictionary of querystring data to attach to the Request, defaults to None
|
|
139
|
+
:type params: dict, optional
|
|
140
|
+
|
|
141
|
+
:raises APIException: an api exception with message and error type code
|
|
142
|
+
|
|
143
|
+
:return: Response object
|
|
144
|
+
:rtype: requests.Response
|
|
145
|
+
"""
|
|
146
|
+
self._refresh_token_if_expired()
|
|
147
|
+
|
|
148
|
+
url = self._add_base_url(url)
|
|
149
|
+
headers = self._generate_headers()
|
|
150
|
+
|
|
151
|
+
response = requests.patch(url, json=json, headers=headers, params=params, **kwargs)
|
|
152
|
+
handle_error(response)
|
|
153
|
+
|
|
154
|
+
return response
|
|
155
|
+
|
|
156
|
+
def delete(
|
|
157
|
+
self, url: str, json: dict | None = None, params: dict | None = None, **kwargs
|
|
158
|
+
) -> requests.Response:
|
|
159
|
+
"""Sends a DELETE request.
|
|
160
|
+
|
|
161
|
+
A wrapper for the requests.delete method.
|
|
162
|
+
|
|
163
|
+
Builds the url, uses custom headers, refresh tokens if needed.
|
|
164
|
+
|
|
165
|
+
:param url: relative url of the API endpoint
|
|
166
|
+
:type url: str
|
|
167
|
+
:param json: A JSON serializable Python object to send in the body of the Request, defaults to None
|
|
168
|
+
:type json: dict, optional
|
|
169
|
+
:param params: Dictionary of querystring data to attach to the Request, defaults to None
|
|
170
|
+
:type params: dict, optional
|
|
171
|
+
|
|
172
|
+
:raises APIException: an api exception with message and error type code
|
|
173
|
+
|
|
174
|
+
:return: Response object
|
|
175
|
+
:rtype: requests.Response
|
|
176
|
+
"""
|
|
177
|
+
self._refresh_token_if_expired()
|
|
178
|
+
|
|
179
|
+
url = self._add_base_url(url)
|
|
180
|
+
headers = self._generate_headers()
|
|
181
|
+
|
|
182
|
+
response = requests.delete(url, headers=headers, json=json, params=params, **kwargs)
|
|
183
|
+
handle_error(response)
|
|
184
|
+
|
|
185
|
+
return response
|
|
186
|
+
|
|
187
|
+
def _refresh_token_if_expired(self) -> None:
|
|
188
|
+
"""Refreshes the access token if it expired.
|
|
189
|
+
|
|
190
|
+
Uses the refresh token to refresh, and if the refresh token is also expired, uses the client credentials.
|
|
191
|
+
|
|
192
|
+
:raises APIException: an api exception with message and error type code
|
|
193
|
+
"""
|
|
194
|
+
if self._auth_service.is_expired():
|
|
195
|
+
# try to refresh. if refresh token has expired, reauthenticate
|
|
196
|
+
try:
|
|
197
|
+
self._auth_service.refresh()
|
|
198
|
+
except Exception:
|
|
199
|
+
self._auth_service.authenticate()
|
|
200
|
+
|
|
201
|
+
def _generate_headers(self) -> dict:
|
|
202
|
+
"""Generate the default headers for every request.
|
|
203
|
+
|
|
204
|
+
:return: dict with request headers
|
|
205
|
+
:rtype: dict
|
|
206
|
+
"""
|
|
207
|
+
headers = {
|
|
208
|
+
'Authorization': self._generate_bearer_header(),
|
|
209
|
+
'User-Agent': self._generate_user_agent(),
|
|
210
|
+
'Content-Type': 'application/json',
|
|
211
|
+
}
|
|
212
|
+
return headers
|
|
213
|
+
|
|
214
|
+
def _generate_bearer_header(self) -> str:
|
|
215
|
+
"""Generate the authorization header Bearer string.
|
|
216
|
+
|
|
217
|
+
:return: Authorization header Bearer string
|
|
218
|
+
:rtype: str
|
|
219
|
+
"""
|
|
220
|
+
return f'Bearer {self._auth_service._access_token}'
|
|
221
|
+
|
|
222
|
+
def _generate_user_agent(self) -> str:
|
|
223
|
+
"""Generate the user agent string.
|
|
224
|
+
|
|
225
|
+
:return: user agent string
|
|
226
|
+
:rtype: str
|
|
227
|
+
"""
|
|
228
|
+
# get the first 10 chars of the client id
|
|
229
|
+
client_id_truncated = self._auth_service._client_id[:10]
|
|
230
|
+
|
|
231
|
+
return f'datacrunch-python-v{self._version}-{client_id_truncated}'
|
|
232
|
+
|
|
233
|
+
def _add_base_url(self, url: str) -> str:
|
|
234
|
+
"""Adds the base url to the relative url.
|
|
235
|
+
|
|
236
|
+
Example:
|
|
237
|
+
if the relative url is '/balance'
|
|
238
|
+
and the base url is 'https://api.verda.com/v1'
|
|
239
|
+
then this method will return 'https://api.verda.com/v1/balance'
|
|
240
|
+
|
|
241
|
+
:param url: a relative url path
|
|
242
|
+
:type url: str
|
|
243
|
+
:return: the full url path
|
|
244
|
+
:rtype: str
|
|
245
|
+
"""
|
|
246
|
+
return self._base_url + url
|
verda/images/__init__.py
ADDED
|
File without changes
|
verda/images/images.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from verda.helpers import stringify_class_object_properties
|
|
2
|
+
|
|
3
|
+
IMAGES_ENDPOINT = '/images'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Image:
|
|
7
|
+
"""An image model class."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, id: str, name: str, image_type: str, details: list[str]) -> None:
|
|
10
|
+
"""Initialize an image object.
|
|
11
|
+
|
|
12
|
+
:param id: image id
|
|
13
|
+
:type id: str
|
|
14
|
+
:param name: image name
|
|
15
|
+
:type name: str
|
|
16
|
+
:param image_type: image type, e.g. 'ubuntu-20.04-cuda-11.0'
|
|
17
|
+
:type image_type: str
|
|
18
|
+
:param details: image details
|
|
19
|
+
:type details: list[str]
|
|
20
|
+
"""
|
|
21
|
+
self._id = id
|
|
22
|
+
self._name = name
|
|
23
|
+
self._image_type = image_type
|
|
24
|
+
self._details = details
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def id(self) -> str:
|
|
28
|
+
"""Get the image id.
|
|
29
|
+
|
|
30
|
+
:return: image id
|
|
31
|
+
:rtype: str
|
|
32
|
+
"""
|
|
33
|
+
return self._id
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def name(self) -> str:
|
|
37
|
+
"""Get the image name.
|
|
38
|
+
|
|
39
|
+
:return: image name
|
|
40
|
+
:rtype: str
|
|
41
|
+
"""
|
|
42
|
+
return self._name
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def image_type(self) -> str:
|
|
46
|
+
"""Get the image type.
|
|
47
|
+
|
|
48
|
+
:return: image type
|
|
49
|
+
:rtype: str
|
|
50
|
+
"""
|
|
51
|
+
return self._image_type
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def details(self) -> list[str]:
|
|
55
|
+
"""Get the image details.
|
|
56
|
+
|
|
57
|
+
:return: image details
|
|
58
|
+
:rtype: list[str]
|
|
59
|
+
"""
|
|
60
|
+
return self._details
|
|
61
|
+
|
|
62
|
+
def __str__(self) -> str:
|
|
63
|
+
"""Returns a string of the json representation of the image.
|
|
64
|
+
|
|
65
|
+
:return: json representation of the image
|
|
66
|
+
:rtype: str
|
|
67
|
+
"""
|
|
68
|
+
return stringify_class_object_properties(self)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ImagesService:
|
|
72
|
+
"""A service for interacting with the images endpoint."""
|
|
73
|
+
|
|
74
|
+
def __init__(self, http_client) -> None:
|
|
75
|
+
self._http_client = http_client
|
|
76
|
+
|
|
77
|
+
def get(self) -> list[Image]:
|
|
78
|
+
"""Get the available instance images.
|
|
79
|
+
|
|
80
|
+
:return: list of images objects
|
|
81
|
+
:rtype: list[Image]
|
|
82
|
+
"""
|
|
83
|
+
images = self._http_client.get(IMAGES_ENDPOINT).json()
|
|
84
|
+
image_objects = [
|
|
85
|
+
Image(image['id'], image['name'], image['image_type'], image['details'])
|
|
86
|
+
for image in images
|
|
87
|
+
]
|
|
88
|
+
return image_objects
|
|
File without changes
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from dataclasses_json import dataclass_json
|
|
4
|
+
|
|
5
|
+
INSTANCE_TYPES_ENDPOINT = '/instance-types'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass_json
|
|
9
|
+
@dataclass
|
|
10
|
+
class InstanceType:
|
|
11
|
+
"""Instance type.
|
|
12
|
+
|
|
13
|
+
Attributes:
|
|
14
|
+
id: instance type id.
|
|
15
|
+
instance_type: instance type, e.g. '8V100.48M'.
|
|
16
|
+
price_per_hour: instance type price per hour.
|
|
17
|
+
spot_price_per_hour: instance type spot price per hour.
|
|
18
|
+
description: instance type description.
|
|
19
|
+
cpu: instance type cpu details.
|
|
20
|
+
gpu: instance type gpu details.
|
|
21
|
+
memory: instance type memory details.
|
|
22
|
+
gpu_memory: instance type gpu memory details.
|
|
23
|
+
storage: instance type storage details.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
id: str
|
|
27
|
+
instance_type: str
|
|
28
|
+
price_per_hour: float
|
|
29
|
+
spot_price_per_hour: float
|
|
30
|
+
description: str
|
|
31
|
+
cpu: dict
|
|
32
|
+
gpu: dict
|
|
33
|
+
memory: dict
|
|
34
|
+
gpu_memory: dict
|
|
35
|
+
storage: dict
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class InstanceTypesService:
|
|
39
|
+
"""A service for interacting with the instance-types endpoint."""
|
|
40
|
+
|
|
41
|
+
def __init__(self, http_client) -> None:
|
|
42
|
+
self._http_client = http_client
|
|
43
|
+
|
|
44
|
+
def get(self) -> list[InstanceType]:
|
|
45
|
+
"""Get all instance types.
|
|
46
|
+
|
|
47
|
+
:return: list of instance type objects
|
|
48
|
+
:rtype: list[InstanceType]
|
|
49
|
+
"""
|
|
50
|
+
instance_types = self._http_client.get(INSTANCE_TYPES_ENDPOINT).json()
|
|
51
|
+
instance_type_objects = [
|
|
52
|
+
InstanceType(
|
|
53
|
+
id=instance_type['id'],
|
|
54
|
+
instance_type=instance_type['instance_type'],
|
|
55
|
+
price_per_hour=float(instance_type['price_per_hour']),
|
|
56
|
+
spot_price_per_hour=float(instance_type['spot_price']),
|
|
57
|
+
description=instance_type['description'],
|
|
58
|
+
cpu=instance_type['cpu'],
|
|
59
|
+
gpu=instance_type['gpu'],
|
|
60
|
+
memory=instance_type['memory'],
|
|
61
|
+
gpu_memory=instance_type['gpu_memory'],
|
|
62
|
+
storage=instance_type['storage'],
|
|
63
|
+
)
|
|
64
|
+
for instance_type in instance_types
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
return instance_type_objects
|
|
File without changes
|