cgcsdk 1.0.4__py3-none-any.whl → 1.0.5__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.
- cgc/.env +1 -1
- cgc/CHANGELOG.md +26 -1
- cgc/commands/cgc_cmd.py +7 -0
- cgc/commands/cgc_helpers.py +45 -33
- cgc/commands/compute/compute_cmd.py +114 -0
- cgc/commands/compute/compute_models.py +3 -1
- cgc/commands/compute/compute_responses.py +17 -1
- cgc/commands/compute/compute_utills.py +94 -2
- cgc/commands/resource/resource_cmd.py +1 -1
- cgc/sdk/__init__.py +3 -0
- cgc/sdk/exceptions.py +7 -0
- cgc/sdk/resource.py +425 -0
- cgc/utils/consts/env_consts.py +1 -0
- cgc/utils/custom_exceptions.py +1 -1
- cgc/utils/response_utils.py +60 -0
- {cgcsdk-1.0.4.dist-info → cgcsdk-1.0.5.dist-info}/METADATA +1 -1
- {cgcsdk-1.0.4.dist-info → cgcsdk-1.0.5.dist-info}/RECORD +21 -19
- {cgcsdk-1.0.4.dist-info → cgcsdk-1.0.5.dist-info}/LICENSE +0 -0
- {cgcsdk-1.0.4.dist-info → cgcsdk-1.0.5.dist-info}/WHEEL +0 -0
- {cgcsdk-1.0.4.dist-info → cgcsdk-1.0.5.dist-info}/entry_points.txt +0 -0
- {cgcsdk-1.0.4.dist-info → cgcsdk-1.0.5.dist-info}/top_level.txt +0 -0
cgc/.env
CHANGED
cgc/CHANGELOG.md
CHANGED
@@ -1,5 +1,30 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 1.0.5
|
4
|
+
|
5
|
+
Release on March 7, 2024
|
6
|
+
|
7
|
+
* added new compute template: custom
|
8
|
+
* custom template can be any docker image
|
9
|
+
* custom template can use docker registry, that requires authentication
|
10
|
+
* during compute create, new flag available: -fp, --full-path
|
11
|
+
* allows to mount volume with full path; works with one volume only
|
12
|
+
* added support for adding for compute create config_map_data
|
13
|
+
* added for compute create
|
14
|
+
* --startup-command flag to set startup command (overrides default command in Dockerfile)
|
15
|
+
* --repository-secret flag to set docker registry secret from namespace
|
16
|
+
* --image flag to set docker image for custom template
|
17
|
+
* managing compute ports, require user confirmation
|
18
|
+
* resource commands
|
19
|
+
* add compute custom create and resource delete to cgc sdk
|
20
|
+
* add compute port add, delete, update, list to cgc sdk
|
21
|
+
* add compute and db **list** to cgc sdk
|
22
|
+
* add resource ready command to cgc sdk
|
23
|
+
* on-premises support for node_port_enabled
|
24
|
+
* added new command: cgc context replace-env "path_to_env_file"
|
25
|
+
* added new command: cgc context get-env "where_to_save_env_file"
|
26
|
+
* added new command: cgc compute list-mounts
|
27
|
+
|
3
28
|
## 1.0.4
|
4
29
|
|
5
30
|
Release on December 12, 2023
|
@@ -10,7 +35,7 @@ Release on December 12, 2023
|
|
10
35
|
* add support for: weaviate (db, no gpu), t2v-transformers (compute, gpu enabled)
|
11
36
|
* weaviate can work with t2v-transformers
|
12
37
|
* requires added -d flag: weaviate_enable_modules=text2vec-transformers
|
13
|
-
* requires added -d flag: weaviate_transformers_inference_api
|
38
|
+
* requires added -d flag: weaviate_transformers_inference_api=<http://name-of-t2v-app:8080>
|
14
39
|
|
15
40
|
## 1.0.3
|
16
41
|
|
cgc/commands/cgc_cmd.py
CHANGED
@@ -13,6 +13,7 @@ from cgc.commands.auth.auth_cmd import auth_register
|
|
13
13
|
from cgc.utils import set_environment_data, check_if_config_exist, list_all_config_files
|
14
14
|
from cgc.commands.cgc_helpers import table_of_user_context_files
|
15
15
|
from cgc.utils.config_utils import config_path
|
16
|
+
from cgc.utils.consts.env_consts import ENV_FILE_PATH
|
16
17
|
|
17
18
|
|
18
19
|
@click.command("rm", cls=CustomCommand)
|
@@ -115,3 +116,9 @@ def folder_of_contexts():
|
|
115
116
|
click.echo(
|
116
117
|
"You can check location of that folder on different machine with the same command."
|
117
118
|
)
|
119
|
+
|
120
|
+
|
121
|
+
@context_group.command("get-env-path", cls=CustomCommand)
|
122
|
+
def get_env_path():
|
123
|
+
"""Displays current environment file path"""
|
124
|
+
click.echo(f"Current environment file path: {ENV_FILE_PATH}")
|
cgc/commands/cgc_helpers.py
CHANGED
@@ -1,33 +1,45 @@
|
|
1
|
-
from typing import List
|
2
|
-
from cgc.utils import quick_sort
|
3
|
-
from cgc.utils.config_utils import read_from_cfg
|
4
|
-
from tabulate import tabulate
|
5
|
-
|
6
|
-
|
7
|
-
def table_of_user_context_files(config_files: List[str]):
|
8
|
-
# print tabulate of: [context NR | namespace | user_id]
|
9
|
-
headers = ["Context No.", "Namespace", "User ID"]
|
10
|
-
contexts = []
|
11
|
-
contexts_nrs = []
|
12
|
-
for file in config_files:
|
13
|
-
file_context = []
|
14
|
-
file_context.append(
|
15
|
-
int(file.split(".")[0]) if file != "cfg.json" else 1
|
16
|
-
) # should never throw exception with good config_file list
|
17
|
-
contexts_nrs.append(file_context[0])
|
18
|
-
file_data = read_from_cfg(None, file)
|
19
|
-
values_to_read = ["namespace", "user_id"]
|
20
|
-
for k in values_to_read:
|
21
|
-
try:
|
22
|
-
value = file_data[k]
|
23
|
-
except KeyError:
|
24
|
-
value = None
|
25
|
-
file_context.append(value)
|
26
|
-
contexts.append(file_context)
|
27
|
-
|
28
|
-
contexts_nrs_sorted = quick_sort(contexts_nrs)
|
29
|
-
contexts_sorted = []
|
30
|
-
for context in contexts_nrs_sorted:
|
31
|
-
contexts_sorted.append(contexts[contexts_nrs.index(context)])
|
32
|
-
|
33
|
-
return tabulate(contexts_sorted, headers=headers)
|
1
|
+
from typing import List
|
2
|
+
from cgc.utils import quick_sort
|
3
|
+
from cgc.utils.config_utils import read_from_cfg
|
4
|
+
from tabulate import tabulate
|
5
|
+
|
6
|
+
|
7
|
+
def table_of_user_context_files(config_files: List[str]):
|
8
|
+
# print tabulate of: [context NR | namespace | user_id]
|
9
|
+
headers = ["Context No.", "Namespace", "User ID"]
|
10
|
+
contexts = []
|
11
|
+
contexts_nrs = []
|
12
|
+
for file in config_files:
|
13
|
+
file_context = []
|
14
|
+
file_context.append(
|
15
|
+
int(file.split(".")[0]) if file != "cfg.json" else 1
|
16
|
+
) # should never throw exception with good config_file list
|
17
|
+
contexts_nrs.append(file_context[0])
|
18
|
+
file_data = read_from_cfg(None, file)
|
19
|
+
values_to_read = ["namespace", "user_id"]
|
20
|
+
for k in values_to_read:
|
21
|
+
try:
|
22
|
+
value = file_data[k]
|
23
|
+
except KeyError:
|
24
|
+
value = None
|
25
|
+
file_context.append(value)
|
26
|
+
contexts.append(file_context)
|
27
|
+
|
28
|
+
contexts_nrs_sorted = quick_sort(contexts_nrs)
|
29
|
+
contexts_sorted = []
|
30
|
+
for context in contexts_nrs_sorted:
|
31
|
+
contexts_sorted.append(contexts[contexts_nrs.index(context)])
|
32
|
+
|
33
|
+
return tabulate(contexts_sorted, headers=headers)
|
34
|
+
|
35
|
+
def process_stdin(input_file: str):
|
36
|
+
full_stdin = []
|
37
|
+
try:
|
38
|
+
for line in input_file:
|
39
|
+
line = line.strip()
|
40
|
+
if line and line != '|':
|
41
|
+
full_stdin.append(line.rstrip('\n')) # Remove newline characters
|
42
|
+
except TypeError:
|
43
|
+
return None
|
44
|
+
return " ".join(full_stdin)
|
45
|
+
|
@@ -1,10 +1,13 @@
|
|
1
1
|
import json
|
2
|
+
import sys
|
2
3
|
import click
|
4
|
+
from cgc.commands.cgc_helpers import process_stdin
|
3
5
|
|
4
6
|
from cgc.commands.compute.compute_models import ComputesList, GPUsList
|
5
7
|
from cgc.commands.compute.compute_responses import (
|
6
8
|
compute_create_filebrowser_response,
|
7
9
|
compute_create_response,
|
10
|
+
compute_list_mounts_response,
|
8
11
|
compute_list_response,
|
9
12
|
get_compute_port_list,
|
10
13
|
)
|
@@ -96,6 +99,16 @@ def compute_filebrowser_delete():
|
|
96
99
|
)
|
97
100
|
def compute_port_add(app_name: str, port_name: str, port: int, ingress: bool):
|
98
101
|
"""Add a port to a running resource"""
|
102
|
+
while True:
|
103
|
+
click.echo(
|
104
|
+
'Adding a port to a running resource will expose it to the internet. If you want to add a port without exposing it to the internet, use the "--no-ingress" flag.'
|
105
|
+
)
|
106
|
+
click.echo("Resource will be restarted after port addition.")
|
107
|
+
answer = input("Do you want to continue? (Y/N): ").lower()
|
108
|
+
if answer in ("y", "yes"):
|
109
|
+
break
|
110
|
+
if answer in ("n", "no"):
|
111
|
+
sys.exit()
|
99
112
|
api_url, headers = get_api_url_and_prepare_headers()
|
100
113
|
url = f"{api_url}/v1/api/resource/ports?port_modification_mode=ADD"
|
101
114
|
metric = "resource.ports.add"
|
@@ -148,6 +161,16 @@ def compute_port_add(app_name: str, port_name: str, port: int, ingress: bool):
|
|
148
161
|
)
|
149
162
|
def compute_port_update(app_name: str, port_name: str, port: int, ingress: bool):
|
150
163
|
"""Update a port in a running resource"""
|
164
|
+
while True:
|
165
|
+
click.echo(
|
166
|
+
'Updating a port in a running resource will expose it to the internet. If you want to update a port without exposing it to the internet, use the "--no-ingress" flag.'
|
167
|
+
)
|
168
|
+
click.echo("Resource will be restarted after port update.")
|
169
|
+
answer = input("Do you want to continue? (Y/N): ").lower()
|
170
|
+
if answer in ("y", "yes"):
|
171
|
+
break
|
172
|
+
if answer in ("n", "no"):
|
173
|
+
sys.exit()
|
151
174
|
api_url, headers = get_api_url_and_prepare_headers()
|
152
175
|
url = f"{api_url}/v1/api/resource/ports?port_modification_mode=UPDATE"
|
153
176
|
metric = "resource.ports.update"
|
@@ -178,6 +201,16 @@ def compute_port_update(app_name: str, port_name: str, port: int, ingress: bool)
|
|
178
201
|
)
|
179
202
|
def compute_port_delete(app_name: str, port_name: str):
|
180
203
|
"""Delete a port from a running resource"""
|
204
|
+
while True:
|
205
|
+
click.echo(
|
206
|
+
'Deleting a port from a running resource will expose it to the internet. If you want to delete a port without exposing it to the internet, use the "--no-ingress" flag.'
|
207
|
+
)
|
208
|
+
click.echo("Resource will be restarted after port delete.")
|
209
|
+
answer = input("Do you want to continue? (Y/N): ").lower()
|
210
|
+
if answer in ("y", "yes"):
|
211
|
+
break
|
212
|
+
if answer in ("n", "no"):
|
213
|
+
sys.exit()
|
181
214
|
api_url, headers = get_api_url_and_prepare_headers()
|
182
215
|
url = f"{api_url}/v1/api/resource/ports?port_modification_mode=DELETE"
|
183
216
|
metric = "resource.ports.delete"
|
@@ -218,6 +251,7 @@ def compute_port_list(app_name: str):
|
|
218
251
|
|
219
252
|
@compute_group.command("create", cls=CustomCommand)
|
220
253
|
@click.argument("entity", type=click.Choice(ComputesList.get_list()))
|
254
|
+
@click.argument("startup_command", type=click.File("r"), default="-", required=False)
|
221
255
|
@click.option(
|
222
256
|
"-n", "--name", "name", type=click.STRING, required=True, help="Desired app name"
|
223
257
|
)
|
@@ -260,6 +294,13 @@ def compute_port_list(app_name: str):
|
|
260
294
|
multiple=True,
|
261
295
|
help="List of volume names to be mounted with default mount path",
|
262
296
|
)
|
297
|
+
@click.option(
|
298
|
+
"-fp",
|
299
|
+
"--full-path",
|
300
|
+
"volume_full_path",
|
301
|
+
type=click.STRING,
|
302
|
+
help="If set, full path will be used for volume mount. Valid for 1 volume.",
|
303
|
+
)
|
263
304
|
@click.option(
|
264
305
|
"-d",
|
265
306
|
"--resource-data",
|
@@ -267,6 +308,25 @@ def compute_port_list(app_name: str):
|
|
267
308
|
multiple=True,
|
268
309
|
help="List of optional arguments to be passed to the app, key=value format",
|
269
310
|
)
|
311
|
+
@click.option(
|
312
|
+
"--image",
|
313
|
+
"image_name",
|
314
|
+
type=click.STRING,
|
315
|
+
help="Image to be used by the app",
|
316
|
+
)
|
317
|
+
@click.option(
|
318
|
+
"--repository-secret",
|
319
|
+
"repository_secret",
|
320
|
+
type=click.STRING,
|
321
|
+
help="Use secret to pull image from private repository",
|
322
|
+
)
|
323
|
+
@click.option(
|
324
|
+
"-cm",
|
325
|
+
"--config-map",
|
326
|
+
"config_maps_data",
|
327
|
+
multiple=True,
|
328
|
+
help="List of optional arguments to be passed to the app, key=value format",
|
329
|
+
)
|
270
330
|
@click.option(
|
271
331
|
"--shm",
|
272
332
|
"shm_size",
|
@@ -274,6 +334,14 @@ def compute_port_list(app_name: str):
|
|
274
334
|
default=0,
|
275
335
|
help="Size of shared memory in Gi",
|
276
336
|
)
|
337
|
+
@click.option(
|
338
|
+
"--node-port-enabled",
|
339
|
+
"node_port_enabled",
|
340
|
+
type=click.BOOL,
|
341
|
+
default=False,
|
342
|
+
is_flag=True,
|
343
|
+
help="If set, app will be exposed to the internet",
|
344
|
+
)
|
277
345
|
def compute_create(
|
278
346
|
entity: str,
|
279
347
|
gpu: int,
|
@@ -281,9 +349,15 @@ def compute_create(
|
|
281
349
|
cpu: int,
|
282
350
|
memory: int,
|
283
351
|
volumes: list[str],
|
352
|
+
volume_full_path: str,
|
284
353
|
resource_data: list[str],
|
354
|
+
config_maps_data: list[str],
|
285
355
|
name: str,
|
286
356
|
shm_size: int,
|
357
|
+
image_name: str,
|
358
|
+
startup_command: str,
|
359
|
+
repository_secret: str,
|
360
|
+
node_port_enabled: bool,
|
287
361
|
):
|
288
362
|
"""
|
289
363
|
Create an app in user namespace.
|
@@ -298,15 +372,29 @@ def compute_create(
|
|
298
372
|
:type memory: int
|
299
373
|
:param volumes: list of volumes to mount
|
300
374
|
:type volumes: list[str]
|
375
|
+
:param volume_full_path: if set, full path will be used for volume mount
|
376
|
+
:type volume_full_path: str
|
301
377
|
:param resource_data: list of optional arguments to be passed to the app
|
302
378
|
:type resource_data: list[str]
|
379
|
+
:param config_maps_data: list of optional arguments to be passed to the app
|
380
|
+
:type config_maps_data: list[str]
|
303
381
|
:param name: name of app
|
304
382
|
:type name: str
|
305
383
|
:param shm_size: size of shared memory
|
306
384
|
:type shm_size: int
|
385
|
+
:param image_name: name of image to be used by the app
|
386
|
+
:type image_name: str
|
387
|
+
:param startup_command: command to be executed on app startup; it is stdin input
|
388
|
+
:type startup_command: str
|
389
|
+
:param repository_secret: use secret to pull image from private repository
|
390
|
+
:type repository_secret: str
|
391
|
+
:param node_port_enabled: if set, app ports will be exposed to the network via node addresses
|
392
|
+
:type node_port_enabled: bool
|
307
393
|
"""
|
308
394
|
api_url, headers = get_api_url_and_prepare_headers()
|
309
395
|
url = f"{api_url}/v1/api/resource/create"
|
396
|
+
if startup_command != "-":
|
397
|
+
startup_command = process_stdin(startup_command)
|
310
398
|
metric = "compute.create"
|
311
399
|
__payload = compute_create_payload(
|
312
400
|
name=name,
|
@@ -315,9 +403,15 @@ def compute_create(
|
|
315
403
|
memory=memory,
|
316
404
|
gpu=gpu,
|
317
405
|
volumes=volumes,
|
406
|
+
volume_full_path=volume_full_path,
|
318
407
|
resource_data=resource_data,
|
408
|
+
config_maps_data=config_maps_data,
|
319
409
|
gpu_type=gpu_type,
|
320
410
|
shm_size=shm_size,
|
411
|
+
image_name=image_name,
|
412
|
+
startup_command=startup_command,
|
413
|
+
repository_secret=repository_secret,
|
414
|
+
node_port_enabled=node_port_enabled,
|
321
415
|
)
|
322
416
|
# Extra keys allowed for payload:
|
323
417
|
# 1.
|
@@ -395,3 +489,23 @@ def resource_list(detailed: bool):
|
|
395
489
|
)
|
396
490
|
|
397
491
|
click.echo(table)
|
492
|
+
|
493
|
+
|
494
|
+
@compute_group.command("list-mounts", cls=CustomCommand)
|
495
|
+
def compute_list_mounts():
|
496
|
+
"""
|
497
|
+
List all apps for user namespace.
|
498
|
+
"""
|
499
|
+
api_url, headers = get_api_url_and_prepare_headers()
|
500
|
+
url = f"{api_url}/v1/api/resource/list?resource_type=compute"
|
501
|
+
metric = "compute.list"
|
502
|
+
__res = call_api(
|
503
|
+
request=EndpointTypes.get,
|
504
|
+
url=url,
|
505
|
+
headers=headers,
|
506
|
+
)
|
507
|
+
table = compute_list_mounts_response(
|
508
|
+
retrieve_and_validate_response_send_metric(__res, metric),
|
509
|
+
)
|
510
|
+
|
511
|
+
click.echo(table)
|
@@ -26,6 +26,7 @@ class ComputesList(CGCEntityList):
|
|
26
26
|
RAG = "rag"
|
27
27
|
DEEPSTREAM = "deepstream"
|
28
28
|
T2V_TRANSFORMERS = "t2v-transformers"
|
29
|
+
CUSTOM = "custom"
|
29
30
|
|
30
31
|
|
31
32
|
class DatabasesList(CGCEntityList):
|
@@ -49,6 +50,7 @@ class GPUsList(CGCEntityList):
|
|
49
50
|
:type Enum: str
|
50
51
|
"""
|
51
52
|
|
53
|
+
V100 = "V100"
|
52
54
|
A100 = "A100"
|
53
|
-
# V100 = "V100"
|
54
55
|
A5000 = "A5000"
|
56
|
+
H100 = "H100"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from cgc.telemetry.basic import change_gauge
|
2
2
|
from cgc.telemetry.basic import setup_gauge
|
3
|
-
from cgc.commands.compute.compute_utills import get_app_list
|
3
|
+
from cgc.commands.compute.compute_utills import get_app_list, get_app_mounts
|
4
4
|
from cgc.utils.config_utils import get_namespace
|
5
5
|
from cgc.utils.message_utils import key_error_decorator_for_helpers
|
6
6
|
from cgc.commands.compute import NoAppsToList
|
@@ -55,6 +55,22 @@ def compute_list_response(detailed: bool, data: dict) -> str:
|
|
55
55
|
return tabulate_a_response(table)
|
56
56
|
|
57
57
|
|
58
|
+
@key_error_decorator_for_helpers
|
59
|
+
def compute_list_mounts_response(data: dict) -> str:
|
60
|
+
"""Create response string for compute list_mounts command.
|
61
|
+
|
62
|
+
:param response: dict object from API response.
|
63
|
+
:type response: requests.Response
|
64
|
+
:return: Response string.
|
65
|
+
:rtype: str
|
66
|
+
"""
|
67
|
+
pod_list = data["details"]["pods_list"]
|
68
|
+
list_of_json_data = get_app_mounts(pod_list)
|
69
|
+
table = fill_missing_values_in_a_response(list_of_json_data)
|
70
|
+
|
71
|
+
return tabulate_a_response(table)
|
72
|
+
|
73
|
+
|
58
74
|
@key_error_decorator_for_helpers
|
59
75
|
def compute_create_filebrowser_response(data: dict) -> str:
|
60
76
|
"""Create response string for compute create_filebrowser command
|
@@ -1,3 +1,25 @@
|
|
1
|
+
from ast import main
|
2
|
+
import cgc.utils.consts.env_consts as env_consts
|
3
|
+
|
4
|
+
def list_get_mounted_volumes_paths(volume_list: list) -> str:
|
5
|
+
"""Formats and returns list of PVC volumes mounted to an app.
|
6
|
+
|
7
|
+
:param volume_list: list of all volumes mounted to an app
|
8
|
+
:type volume_list: list
|
9
|
+
:return: list of volume paths
|
10
|
+
:rtype: str
|
11
|
+
"""
|
12
|
+
volume_name_list = []
|
13
|
+
for volume in volume_list:
|
14
|
+
volume_type = volume.get("type")
|
15
|
+
if volume_type == "PVC":
|
16
|
+
volume_mount_path = volume.get("mount_path")
|
17
|
+
volume_name_list.append(volume_mount_path)
|
18
|
+
volumes_mounted = (
|
19
|
+
", ".join(volume_name_list) if len(volume_name_list) != 0 else None
|
20
|
+
)
|
21
|
+
return volumes_mounted
|
22
|
+
|
1
23
|
def list_get_mounted_volumes(volume_list: list) -> str:
|
2
24
|
"""Formats and returns list of PVC volumes mounted to an app.
|
3
25
|
|
@@ -17,6 +39,29 @@ def list_get_mounted_volumes(volume_list: list) -> str:
|
|
17
39
|
)
|
18
40
|
return volumes_mounted
|
19
41
|
|
42
|
+
def get_app_mounts(pod_list:list) -> list:
|
43
|
+
output_data = []
|
44
|
+
|
45
|
+
for pod in pod_list:
|
46
|
+
try:
|
47
|
+
main_container_name = pod["labels"]["entity"]
|
48
|
+
try:
|
49
|
+
main_container = [x for x in pod["containers"] if x["name"] == main_container_name][0]
|
50
|
+
except IndexError:
|
51
|
+
raise Exception("Parser was unable to find main container in server output in container list")
|
52
|
+
volumes_mounted = list_get_mounted_volumes(main_container["mounts"])
|
53
|
+
volumes_paths = list_get_mounted_volumes_paths(main_container["mounts"])
|
54
|
+
pod_data = {
|
55
|
+
"name": pod["labels"]["app-name"],
|
56
|
+
"type": pod["labels"]["entity"],
|
57
|
+
"status": pod["status"],
|
58
|
+
"volumes_mounted": volumes_mounted,
|
59
|
+
"volumes_paths": volumes_paths,
|
60
|
+
}
|
61
|
+
output_data.append(pod_data)
|
62
|
+
except KeyError:
|
63
|
+
pass
|
64
|
+
return output_data
|
20
65
|
|
21
66
|
def get_app_list(pod_list: list, detailed: bool) -> list:
|
22
67
|
"""Formats and returns list of apps to print.
|
@@ -117,10 +162,16 @@ def compute_create_payload(
|
|
117
162
|
cpu,
|
118
163
|
memory,
|
119
164
|
volumes: list,
|
165
|
+
volume_full_path: str,
|
120
166
|
resource_data: list = [],
|
167
|
+
config_maps_data: list = [],
|
121
168
|
gpu: int = 0,
|
122
169
|
gpu_type: str = None,
|
123
170
|
shm_size: int = 0,
|
171
|
+
image_name: str = "",
|
172
|
+
startup_command: str = "",
|
173
|
+
repository_secret: str = "",
|
174
|
+
node_port_enabled: bool = False,
|
124
175
|
):
|
125
176
|
"""
|
126
177
|
Create payload for app creation.
|
@@ -137,17 +188,31 @@ def compute_create_payload(
|
|
137
188
|
"gpu": gpu,
|
138
189
|
"memory": memory,
|
139
190
|
"gpu_type": gpu_type,
|
191
|
+
"full_mount_path": volume_full_path,
|
140
192
|
**shm_payload,
|
141
193
|
}
|
142
194
|
}
|
143
195
|
try:
|
144
196
|
if len(volumes) != 0:
|
145
|
-
|
197
|
+
if not volume_full_path:
|
198
|
+
payload["resource_data"]["pv_volume"] = volumes
|
199
|
+
elif volume_full_path and len(volumes) != 1:
|
200
|
+
raise Exception(
|
201
|
+
"Volume full path can only be used with a single volume"
|
202
|
+
)
|
203
|
+
else:
|
204
|
+
payload["resource_data"]["pv_volume"] = volumes
|
146
205
|
except TypeError:
|
147
206
|
pass
|
148
207
|
try:
|
208
|
+
resource_data_dict = {"resource_data": {}}
|
209
|
+
if node_port_enabled:
|
210
|
+
if not env_consts.ON_PREMISES:
|
211
|
+
raise Exception(
|
212
|
+
"NodePort is supported in on-premises environments only."
|
213
|
+
)
|
214
|
+
resource_data_dict["resource_data"]["node_port_enabled"] = True
|
149
215
|
if len(resource_data) != 0:
|
150
|
-
resource_data_dict = {"resource_data": {}}
|
151
216
|
for resource in resource_data:
|
152
217
|
try:
|
153
218
|
key, value = resource.split("=")
|
@@ -156,9 +221,36 @@ def compute_create_payload(
|
|
156
221
|
raise Exception(
|
157
222
|
"Invalid resource data format. Use key=value format"
|
158
223
|
)
|
224
|
+
if image_name:
|
225
|
+
resource_data_dict["resource_data"]["custom_image"] = image_name
|
226
|
+
if startup_command:
|
227
|
+
resource_data_dict["resource_data"]["custom_command"] = startup_command
|
228
|
+
if repository_secret:
|
229
|
+
resource_data_dict["resource_data"][
|
230
|
+
"image_pull_secret_name"
|
231
|
+
] = repository_secret
|
232
|
+
if resource_data_dict["resource_data"] != {}:
|
159
233
|
payload["template_specific_data"] = resource_data_dict
|
160
234
|
except TypeError:
|
161
235
|
pass
|
236
|
+
try:
|
237
|
+
if len(config_maps_data) != 0:
|
238
|
+
config_maps_data_dict = {}
|
239
|
+
for config_map in config_maps_data:
|
240
|
+
try:
|
241
|
+
key, value = config_map.split(
|
242
|
+
"="
|
243
|
+
) # where key is name of config map and value is data
|
244
|
+
config_maps_data_dict[key] = (
|
245
|
+
value # value is dict, ex.: {"key": "value"}
|
246
|
+
)
|
247
|
+
except ValueError:
|
248
|
+
raise Exception(
|
249
|
+
"Invalid config map data format. Use key=value format"
|
250
|
+
)
|
251
|
+
payload["config_maps_data"] = config_maps_data_dict
|
252
|
+
except TypeError:
|
253
|
+
pass
|
162
254
|
return payload
|
163
255
|
|
164
256
|
|
@@ -22,7 +22,7 @@ from cgc.utils.click_group import CustomGroup, CustomCommand
|
|
22
22
|
from cgc.utils.requests_helper import call_api, EndpointTypes
|
23
23
|
|
24
24
|
|
25
|
-
@click.group(name="resource", cls=CustomGroup, hidden=
|
25
|
+
@click.group(name="resource", cls=CustomGroup, hidden=False)
|
26
26
|
def resource_group():
|
27
27
|
"""
|
28
28
|
Management of templates.
|
cgc/sdk/__init__.py
CHANGED
@@ -1,3 +1,6 @@
|
|
1
1
|
from cgc.sdk.mongodb import get_mongo_access as mongo_client
|
2
2
|
from cgc.sdk.redis import get_redis_access as redis_client
|
3
3
|
from cgc.sdk.postgresql import get_postgresql_access as postgresql_client
|
4
|
+
|
5
|
+
import cgc.sdk.resource as resource
|
6
|
+
import cgc.sdk.exceptions as exceptions
|
cgc/sdk/exceptions.py
ADDED
cgc/sdk/resource.py
ADDED
@@ -0,0 +1,425 @@
|
|
1
|
+
import json as _json
|
2
|
+
import re
|
3
|
+
import cgc.sdk.exceptions as _exceptions
|
4
|
+
import cgc.utils.prepare_headers as _prepare_headers
|
5
|
+
import cgc.commands.compute.compute_utills as _compute_utills
|
6
|
+
import cgc.utils.requests_helper as _requests_helper
|
7
|
+
import cgc.utils.response_utils as _response_utils
|
8
|
+
from enum import Enum as _Enum
|
9
|
+
from cgc.commands.compute.compute_models import GPUsList
|
10
|
+
|
11
|
+
|
12
|
+
def start_function_loop(function, infinite: bool = True, *args, **kwargs):
|
13
|
+
"""
|
14
|
+
Starts a function in a loop until it gets response code 200.
|
15
|
+
|
16
|
+
Args:
|
17
|
+
function: The function to be executed.
|
18
|
+
infinite: A boolean indicating whether the loop should be infinite (default: True).
|
19
|
+
*args: Positional arguments to be passed to the function.
|
20
|
+
**kwargs: Keyword arguments to be passed to the function.
|
21
|
+
|
22
|
+
Raises:
|
23
|
+
_SDKException: If the app fails to start within 10 iterations (when infinite is False).
|
24
|
+
|
25
|
+
Returns:
|
26
|
+
None
|
27
|
+
"""
|
28
|
+
from time import sleep
|
29
|
+
|
30
|
+
counter = 0
|
31
|
+
try:
|
32
|
+
response = function(*args, **kwargs)
|
33
|
+
if type(response) is bool:
|
34
|
+
while not response:
|
35
|
+
counter += 1
|
36
|
+
if not infinite and counter > 10:
|
37
|
+
raise _exceptions.SDKException(-1, response)
|
38
|
+
sleep(5)
|
39
|
+
response = function(*args, **kwargs)
|
40
|
+
elif type(response) is dict:
|
41
|
+
while response["code"] != 200:
|
42
|
+
counter += 1
|
43
|
+
if not infinite and counter > 10:
|
44
|
+
raise _exceptions.SDKException(-1, response)
|
45
|
+
sleep(5)
|
46
|
+
response = function(*args, **kwargs)
|
47
|
+
else:
|
48
|
+
raise _exceptions.SDKException(-1, response)
|
49
|
+
except _exceptions.SDKException as e:
|
50
|
+
import logging
|
51
|
+
|
52
|
+
if e.code == 409:
|
53
|
+
logging.warning(e)
|
54
|
+
else:
|
55
|
+
raise e
|
56
|
+
else:
|
57
|
+
return response
|
58
|
+
|
59
|
+
|
60
|
+
def stop_function_loop(function, infinite: bool = True, *args, **kwargs):
|
61
|
+
"""
|
62
|
+
Stop function loop, proceed on response code 200.
|
63
|
+
|
64
|
+
Args:
|
65
|
+
function: The function to be stopped.
|
66
|
+
infinite (bool): Flag indicating whether the loop should run infinitely or not.
|
67
|
+
*args: Variable length argument list to be passed to the function.
|
68
|
+
**kwargs: Arbitrary keyword arguments to be passed to the function.
|
69
|
+
|
70
|
+
Raises:
|
71
|
+
_SDKException: If the app fails to stop within 10 attempts (when infinite is False).
|
72
|
+
|
73
|
+
Returns:
|
74
|
+
None
|
75
|
+
"""
|
76
|
+
from time import sleep
|
77
|
+
|
78
|
+
counter = 0
|
79
|
+
response = function(*args, **kwargs)
|
80
|
+
if type(response) is bool:
|
81
|
+
while not response:
|
82
|
+
counter += 1
|
83
|
+
if not infinite and counter > 10:
|
84
|
+
raise _exceptions.SDKException(-1, response)
|
85
|
+
sleep(5)
|
86
|
+
response = function(*args, **kwargs)
|
87
|
+
elif type(response) is dict:
|
88
|
+
while response["code"] != 200:
|
89
|
+
counter += 1
|
90
|
+
if not infinite and counter > 10:
|
91
|
+
raise _exceptions.SDKException(-1, response)
|
92
|
+
sleep(5)
|
93
|
+
response = function(*args, **kwargs)
|
94
|
+
else:
|
95
|
+
raise _exceptions.SDKException(-1, response)
|
96
|
+
return response
|
97
|
+
|
98
|
+
|
99
|
+
def compute_list():
|
100
|
+
"""
|
101
|
+
List all compute apps for user namespace.
|
102
|
+
"""
|
103
|
+
api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
|
104
|
+
url = f"{api_url}/v1/api/resource/list?resource_type=compute"
|
105
|
+
metric = "compute.list"
|
106
|
+
__res = _requests_helper.call_api(
|
107
|
+
request=_requests_helper.EndpointTypes.get,
|
108
|
+
url=url,
|
109
|
+
headers=headers,
|
110
|
+
)
|
111
|
+
return _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
|
112
|
+
__res, metric
|
113
|
+
)
|
114
|
+
|
115
|
+
|
116
|
+
class ResourceTypes(_Enum):
|
117
|
+
compute = "compute"
|
118
|
+
db = "db"
|
119
|
+
|
120
|
+
|
121
|
+
def _resource_status_ready(name: str, response: dict):
|
122
|
+
"""
|
123
|
+
Check if a resource is ready.
|
124
|
+
|
125
|
+
Args:
|
126
|
+
name: The name of the resource.
|
127
|
+
response: The response from the API call.
|
128
|
+
|
129
|
+
Returns:
|
130
|
+
The response from the API call.
|
131
|
+
"""
|
132
|
+
for pod in response["details"]["pods_list"]:
|
133
|
+
try:
|
134
|
+
if pod["labels"]["app-name"] == name:
|
135
|
+
return pod["status"] == "Running"
|
136
|
+
except KeyError:
|
137
|
+
return False
|
138
|
+
return False
|
139
|
+
|
140
|
+
|
141
|
+
def resource_ready(
|
142
|
+
name: str, resource_type: ResourceTypes = ResourceTypes.compute
|
143
|
+
) -> bool:
|
144
|
+
"""
|
145
|
+
Check if a resource is ready.
|
146
|
+
|
147
|
+
Args:
|
148
|
+
name: The name of the resource.
|
149
|
+
resource_type: The type of resource to check (default: ResourceTypes.compute).
|
150
|
+
|
151
|
+
Returns:
|
152
|
+
bool: A boolean indicating whether the resource is ready.
|
153
|
+
"""
|
154
|
+
api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
|
155
|
+
url = f"{api_url}/v1/api/resource/list?resource_type={resource_type.value}"
|
156
|
+
metric = "resource.list"
|
157
|
+
__res = _requests_helper.call_api(
|
158
|
+
request=_requests_helper.EndpointTypes.get,
|
159
|
+
url=url,
|
160
|
+
headers=headers,
|
161
|
+
)
|
162
|
+
validated_response = (
|
163
|
+
_response_utils.retrieve_and_validate_response_send_metric_for_sdk(
|
164
|
+
__res, metric
|
165
|
+
)
|
166
|
+
)
|
167
|
+
return _resource_status_ready(name, validated_response)
|
168
|
+
|
169
|
+
|
170
|
+
def db_list():
|
171
|
+
"""
|
172
|
+
List all db apps for user namespace.
|
173
|
+
"""
|
174
|
+
api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
|
175
|
+
url = f"{api_url}/v1/api/resource/list?resource_type=db"
|
176
|
+
metric = "compute.list"
|
177
|
+
__res = _requests_helper.call_api(
|
178
|
+
request=_requests_helper.EndpointTypes.get,
|
179
|
+
url=url,
|
180
|
+
headers=headers,
|
181
|
+
)
|
182
|
+
return _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
|
183
|
+
__res, metric
|
184
|
+
)
|
185
|
+
|
186
|
+
|
187
|
+
def compute_create_custom(
|
188
|
+
name: str,
|
189
|
+
image_name: str,
|
190
|
+
cpu: int = 1,
|
191
|
+
memory: int = 2,
|
192
|
+
shm_size: int = 0,
|
193
|
+
gpu: int = 0,
|
194
|
+
gpu_type: str = "A5000",
|
195
|
+
volumes: list = [],
|
196
|
+
volume_full_path: str = "",
|
197
|
+
startup_command: str = "",
|
198
|
+
repository_secret: str = "",
|
199
|
+
resource_data: list = [],
|
200
|
+
config_maps_data: list = [],
|
201
|
+
node_port_enabled: bool = False,
|
202
|
+
):
|
203
|
+
"""
|
204
|
+
Create a custom compute resource.
|
205
|
+
|
206
|
+
:param name: The name of the compute resource.
|
207
|
+
:type name: str
|
208
|
+
:param image_name: The name of the image to use for the compute resource.
|
209
|
+
:type image_name: str,
|
210
|
+
:param cpu: The number of CPUs for the compute resource, defaults to 1.
|
211
|
+
:type cpu: int, optional
|
212
|
+
:param memory: The amount of memory (in GB) for the compute resource, defaults to 2.
|
213
|
+
:type memory: int, optional
|
214
|
+
:param shm_size: The size of the shared memory (in GB) for the compute resource, defaults to 0.
|
215
|
+
:type shm_size: int, optional
|
216
|
+
:param gpu: The number of GPUs for the compute resource, defaults to 0.
|
217
|
+
:type gpu: int, optional
|
218
|
+
:param gpu_type: The type of GPU for the compute resource, defaults to "A5000".
|
219
|
+
:type gpu_type: str, optional
|
220
|
+
:param volumes: The list of volumes to attach to the compute resource, defaults to [].
|
221
|
+
:type volumes: list, optional
|
222
|
+
:param volume_full_path: The full path of the volume, defaults to "".
|
223
|
+
:type volume_full_path: str, optional
|
224
|
+
:param startup_command: The startup command for the compute resource, defaults to "".
|
225
|
+
:type startup_command: str, optional
|
226
|
+
:param repository_secret: The secret for accessing the repository, defaults to "".
|
227
|
+
:type repository_secret: str, optional
|
228
|
+
:param resource_data: The additional resource data, defaults to [].
|
229
|
+
:type resource_data: list, optional
|
230
|
+
:param config_maps_data: The additional config maps data, defaults to [].
|
231
|
+
:type config_maps_data: list, optional
|
232
|
+
:param node_port_enabled: A flag indicating whether the node port is enabled, defaults to False.
|
233
|
+
:type node_port_enabled: bool, optional
|
234
|
+
:raises _SDKException: If the image name is not provided.
|
235
|
+
:raises _SDKException: If an invalid GPU type is specified.
|
236
|
+
:return: The response from the API call.
|
237
|
+
:rtype: _type_
|
238
|
+
"""
|
239
|
+
if not image_name:
|
240
|
+
raise _exceptions.SDKException(-2, "Image name is required")
|
241
|
+
api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
|
242
|
+
url = f"{api_url}/v1/api/resource/create"
|
243
|
+
metric = "compute.create"
|
244
|
+
gpu_type = gpu_type.upper()
|
245
|
+
if gpu_type not in GPUsList.get_list():
|
246
|
+
raise _exceptions.SDKException(-3, f"Invalid GPU type: {gpu_type}")
|
247
|
+
__payload = _compute_utills.compute_create_payload(
|
248
|
+
name=name,
|
249
|
+
entity="custom",
|
250
|
+
cpu=cpu,
|
251
|
+
memory=memory,
|
252
|
+
gpu=gpu,
|
253
|
+
gpu_type=gpu_type,
|
254
|
+
volumes=volumes,
|
255
|
+
volume_full_path=volume_full_path,
|
256
|
+
resource_data=resource_data,
|
257
|
+
config_maps_data=config_maps_data,
|
258
|
+
shm_size=shm_size,
|
259
|
+
image_name=image_name,
|
260
|
+
startup_command=startup_command,
|
261
|
+
repository_secret=repository_secret,
|
262
|
+
node_port_enabled=node_port_enabled,
|
263
|
+
)
|
264
|
+
|
265
|
+
__res = _requests_helper.call_api(
|
266
|
+
request=_requests_helper.EndpointTypes.post,
|
267
|
+
url=url,
|
268
|
+
headers=headers,
|
269
|
+
data=_json.dumps(__payload),
|
270
|
+
)
|
271
|
+
|
272
|
+
return _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
|
273
|
+
__res, metric
|
274
|
+
)
|
275
|
+
|
276
|
+
|
277
|
+
def resource_update_port(
|
278
|
+
name: str, port_name: str, new_port: int, ingress: bool = True
|
279
|
+
):
|
280
|
+
"""
|
281
|
+
Update a port for an app using backend endpoint.
|
282
|
+
|
283
|
+
:param name: name of app to edit
|
284
|
+
:type name: str
|
285
|
+
:param port_name: name of port to edit
|
286
|
+
:type port_name: str
|
287
|
+
:param new_port: new port number
|
288
|
+
:type new_port: int
|
289
|
+
:return: response from the API call
|
290
|
+
:rtype: dict
|
291
|
+
"""
|
292
|
+
api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
|
293
|
+
url = f"{api_url}/v1/api/resource/ports?port_modification_mode=UPDATE"
|
294
|
+
metric = "resource.ports.update"
|
295
|
+
__payload = _compute_utills.port_modification_payload(
|
296
|
+
port_name=port_name,
|
297
|
+
port_number=new_port,
|
298
|
+
ingress=ingress,
|
299
|
+
app_name=name,
|
300
|
+
)
|
301
|
+
__res = _requests_helper.call_api(
|
302
|
+
request=_requests_helper.EndpointTypes.post,
|
303
|
+
url=url,
|
304
|
+
headers=headers,
|
305
|
+
data=_json.dumps(__payload),
|
306
|
+
)
|
307
|
+
|
308
|
+
return _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
|
309
|
+
__res, metric
|
310
|
+
)
|
311
|
+
|
312
|
+
|
313
|
+
def resource_add_port(name: str, port_name: str, new_port: int, ingress: bool = True):
|
314
|
+
"""
|
315
|
+
Add a port for an app using backend endpoint.
|
316
|
+
|
317
|
+
:param name: name of app to edit
|
318
|
+
:type name: str
|
319
|
+
:param port_name: name of port to add
|
320
|
+
:type port_name: str
|
321
|
+
:param new_port: new port number
|
322
|
+
:type new_port: int
|
323
|
+
:return: response from the API call
|
324
|
+
:rtype: dict
|
325
|
+
"""
|
326
|
+
api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
|
327
|
+
url = f"{api_url}/v1/api/resource/ports?port_modification_mode=ADD"
|
328
|
+
metric = "resource.ports.add"
|
329
|
+
__payload = _compute_utills.port_modification_payload(
|
330
|
+
port_name=port_name,
|
331
|
+
port_number=new_port,
|
332
|
+
ingress=ingress,
|
333
|
+
app_name=name,
|
334
|
+
)
|
335
|
+
__res = _requests_helper.call_api(
|
336
|
+
request=_requests_helper.EndpointTypes.post,
|
337
|
+
url=url,
|
338
|
+
headers=headers,
|
339
|
+
data=_json.dumps(__payload),
|
340
|
+
)
|
341
|
+
|
342
|
+
return _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
|
343
|
+
__res, metric
|
344
|
+
)
|
345
|
+
|
346
|
+
|
347
|
+
def resource_delete_port(
|
348
|
+
name: str,
|
349
|
+
port_name: str,
|
350
|
+
):
|
351
|
+
"""
|
352
|
+
Add a port for an app using backend endpoint.
|
353
|
+
|
354
|
+
:param name: name of app to edit
|
355
|
+
:type name: str
|
356
|
+
:param port_name: name of port to delete
|
357
|
+
:type port_name: str
|
358
|
+
:return: response from the API call
|
359
|
+
:rtype: dict
|
360
|
+
"""
|
361
|
+
api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
|
362
|
+
url = f"{api_url}/v1/api/resource/ports?port_modification_mode=DELETE"
|
363
|
+
metric = "resource.ports.delete"
|
364
|
+
__payload = _compute_utills.port_delete_payload(
|
365
|
+
port_name=port_name,
|
366
|
+
app_name=name,
|
367
|
+
)
|
368
|
+
__res = _requests_helper.call_api(
|
369
|
+
request=_requests_helper.EndpointTypes.post,
|
370
|
+
url=url,
|
371
|
+
headers=headers,
|
372
|
+
data=_json.dumps(__payload),
|
373
|
+
)
|
374
|
+
|
375
|
+
return _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
|
376
|
+
__res, metric
|
377
|
+
)
|
378
|
+
|
379
|
+
|
380
|
+
def resource_list_ports(name: str):
|
381
|
+
"""
|
382
|
+
List ports for an app using backend endpoint.
|
383
|
+
|
384
|
+
:param name: name of app to list ports for
|
385
|
+
:type name: str
|
386
|
+
:return: response from the API call
|
387
|
+
:rtype: dict
|
388
|
+
"""
|
389
|
+
api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
|
390
|
+
url = f"{api_url}/v1/api/resource/ports?resource_name={name}"
|
391
|
+
metric = "resource.ports.list"
|
392
|
+
__res = _requests_helper.call_api(
|
393
|
+
request=_requests_helper.EndpointTypes.get,
|
394
|
+
url=url,
|
395
|
+
headers=headers,
|
396
|
+
)
|
397
|
+
|
398
|
+
return _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
|
399
|
+
__res, metric
|
400
|
+
)
|
401
|
+
|
402
|
+
|
403
|
+
def resource_delete(name: str):
|
404
|
+
"""
|
405
|
+
Delete an app using backend endpoint.
|
406
|
+
|
407
|
+
:param name: name of app to delete
|
408
|
+
:type name: str
|
409
|
+
:return: response from the API call
|
410
|
+
:rtype: dict
|
411
|
+
"""
|
412
|
+
api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
|
413
|
+
url = f"{api_url}/v1/api/resource/delete"
|
414
|
+
metric = "resource.delete"
|
415
|
+
__payload = _compute_utills.compute_delete_payload(name=name)
|
416
|
+
__res = _requests_helper.call_api(
|
417
|
+
request=_requests_helper.EndpointTypes.delete,
|
418
|
+
url=url,
|
419
|
+
headers=headers,
|
420
|
+
data=_json.dumps(__payload),
|
421
|
+
)
|
422
|
+
|
423
|
+
return _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
|
424
|
+
__res, metric
|
425
|
+
)
|
cgc/utils/consts/env_consts.py
CHANGED
@@ -38,6 +38,7 @@ TMP_DIR = os.getenv("TMP_DIR")
|
|
38
38
|
RELEASE = int(os.getenv("RELEASE"))
|
39
39
|
MAJOR_VERSION = int(os.getenv("MAJOR_VERSION"))
|
40
40
|
MINOR_VERSION = int(os.getenv("MINOR_VERSION"))
|
41
|
+
ON_PREMISES = True if os.getenv("ON_PREMISES") == "1" else False
|
41
42
|
|
42
43
|
|
43
44
|
def get_config_file_name():
|
cgc/utils/custom_exceptions.py
CHANGED
@@ -14,7 +14,7 @@ CUSTOM_EXCEPTIONS = {
|
|
14
14
|
409: {
|
15
15
|
"PVC_NAME_ALREADY_EXISTS": "Volume with this name already exists.",
|
16
16
|
"PVC_DELETE_EXCEPTION": "Can't delete mounted volume, try with force",
|
17
|
-
"
|
17
|
+
"RESOURCE_TEMPLATE_NAME_ALREADY_EXISTS": "Template with this name already exists.",
|
18
18
|
},
|
19
19
|
404: {
|
20
20
|
"PVC_CREATE_NO_SC": "Selected disk type and access mode unavailable",
|
cgc/utils/response_utils.py
CHANGED
@@ -14,6 +14,7 @@ from cgc.utils.consts.message_consts import (
|
|
14
14
|
from cgc.utils.custom_exceptions import CUSTOM_EXCEPTIONS
|
15
15
|
from cgc.telemetry.basic import increment_metric
|
16
16
|
from cgc.utils.config_utils import get_namespace
|
17
|
+
from cgc.sdk.exceptions import SDKException
|
17
18
|
|
18
19
|
|
19
20
|
def _get_response_json_error_message(response_json: dict):
|
@@ -22,6 +23,65 @@ def _get_response_json_error_message(response_json: dict):
|
|
22
23
|
return UNKNOWN_ERROR
|
23
24
|
|
24
25
|
|
26
|
+
def retrieve_and_validate_response_send_metric_for_sdk(
|
27
|
+
response: requests.Response, metric: str, json_return: bool = True
|
28
|
+
):
|
29
|
+
"""Checks if server is available and user is authorized
|
30
|
+
|
31
|
+
:param response: dict object from API response.
|
32
|
+
:type response: requests.Response
|
33
|
+
"""
|
34
|
+
error_message = UNKNOWN_ERROR
|
35
|
+
try:
|
36
|
+
try:
|
37
|
+
if metric is not None:
|
38
|
+
metric = f"{get_namespace()}.{metric}"
|
39
|
+
except NoNamespaceInConfig:
|
40
|
+
metric = f"unknown-namespace.{metric}"
|
41
|
+
except NoConfigFileFound:
|
42
|
+
print("No config file found. Please use:")
|
43
|
+
print("cgc register")
|
44
|
+
metric = f"bad-client.{metric}"
|
45
|
+
|
46
|
+
if response.status_code == 200:
|
47
|
+
increment_metric(
|
48
|
+
metric=metric, is_error=False
|
49
|
+
) # if metric is None, will not increment metric
|
50
|
+
if json_return:
|
51
|
+
return response.json()
|
52
|
+
else:
|
53
|
+
return response
|
54
|
+
increment_metric(
|
55
|
+
metric=metric, is_error=False
|
56
|
+
) # ALL "VALID" error messages are not Errors
|
57
|
+
if response.status_code == 401:
|
58
|
+
raise SDKException(response.status_code, UNAUTHORIZED_ERROR)
|
59
|
+
elif response.status_code == 302:
|
60
|
+
raise SDKException(response.status_code, ENDPOINT_DISABLED)
|
61
|
+
else:
|
62
|
+
try:
|
63
|
+
response_json = response.json()
|
64
|
+
if "detail" in response_json:
|
65
|
+
raise SDKException(response.status_code, response_json["detail"])
|
66
|
+
else:
|
67
|
+
raise SDKException(
|
68
|
+
response.status_code,
|
69
|
+
CUSTOM_EXCEPTIONS[response.status_code][
|
70
|
+
response_json["reason"]
|
71
|
+
],
|
72
|
+
)
|
73
|
+
except KeyError as e:
|
74
|
+
error_message = _get_response_json_error_message(response_json)
|
75
|
+
increment_metric(metric=metric, is_error=True)
|
76
|
+
raise SDKException(response.status_code, error_message) from e
|
77
|
+
|
78
|
+
except (AttributeError, json.JSONDecodeError) as e:
|
79
|
+
increment_metric(metric=metric, is_error=True)
|
80
|
+
raise SDKException(
|
81
|
+
response.status_code, f"Response reading error. {error_message}"
|
82
|
+
) from e
|
83
|
+
|
84
|
+
|
25
85
|
def retrieve_and_validate_response_send_metric(
|
26
86
|
response: requests.Response, metric: str, json_return: bool = True
|
27
87
|
):
|
@@ -1,12 +1,12 @@
|
|
1
|
-
cgc/.env,sha256=
|
2
|
-
cgc/CHANGELOG.md,sha256=
|
1
|
+
cgc/.env,sha256=4WIMONz4kDhj4OoFOm52Rauw6Ba84NI3HKiCBB3t1-o,209
|
2
|
+
cgc/CHANGELOG.md,sha256=WHHXunoy5D1qKLhG3InzyLPU8gRYtZrLD_jBljBZBLw,7433
|
3
3
|
cgc/__init__.py,sha256=d03Xv8Pw4ktNyUHfmicP6XfxYPXnVYLaCZPyUlg_RNQ,326
|
4
4
|
cgc/cgc.py,sha256=kPLg3h-3kjlMBiwZGOM7yvXJ7pzkVglAbWWQ7KX8jeY,1377
|
5
5
|
cgc/config.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
6
|
cgc/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
-
cgc/commands/cgc_cmd.py,sha256=
|
7
|
+
cgc/commands/cgc_cmd.py,sha256=tct8ZBplLsqD-gjxsnwx_uJo1361iOnZvvxGIGYMU_c,4310
|
8
8
|
cgc/commands/cgc_cmd_responses.py,sha256=ToPjvGf3zUbFLhLwv-BV8ZAvAHg1xS99iprM928MnOY,2026
|
9
|
-
cgc/commands/cgc_helpers.py,sha256=
|
9
|
+
cgc/commands/cgc_helpers.py,sha256=nDq9qLRpF2yu9vvYvDjnIYKudTNdrRop0XAOLYm6zxs,1509
|
10
10
|
cgc/commands/exceptions.py,sha256=l3Sms3D2fxSpLQQQEYeLWxO3to82myTQ0VFgFYdQdLU,45
|
11
11
|
cgc/commands/auth/__init__.py,sha256=K8HkHHotMnK7SQRAst5rx_wprHEphPo_w2KToEymjAY,399
|
12
12
|
cgc/commands/auth/auth_cmd.py,sha256=jfijV-C0uOnpj5LcOgsW7Sn5JiemZ-nuQRQUEjEvaEk,3856
|
@@ -17,27 +17,29 @@ cgc/commands/billing/billing_cmd.py,sha256=T1E5WW5Z-RlzddaotimTuvLvIbGihNpStIBET
|
|
17
17
|
cgc/commands/billing/billing_responses.py,sha256=HAD5N-Odx3Jz1OmhO4v66rHoXpTYIOGlXDsrs0da9dk,1949
|
18
18
|
cgc/commands/billing/billing_utils.py,sha256=zXLbBBcWeOgur-r0OKiIjaKeaxMNxASXWzCTeTsyC6o,4711
|
19
19
|
cgc/commands/compute/__init__.py,sha256=lCdLfZ0ECSHtXEUSwq5YRHH85yXHchSsz8ZJvmClPtI,239
|
20
|
-
cgc/commands/compute/compute_cmd.py,sha256=
|
21
|
-
cgc/commands/compute/compute_models.py,sha256=
|
22
|
-
cgc/commands/compute/compute_responses.py,sha256=
|
23
|
-
cgc/commands/compute/compute_utills.py,sha256=
|
20
|
+
cgc/commands/compute/compute_cmd.py,sha256=_4yt6sGWwbqZSUcc-VjtF2luTwyeQVbu2MsDXpUL1lY,15266
|
21
|
+
cgc/commands/compute/compute_models.py,sha256=DzktT7nR6eG3po6iIayQjnczmnZruAl00Q9o3okqdng,1139
|
22
|
+
cgc/commands/compute/compute_responses.py,sha256=eOmcllyOqPYqN0kSUzSpuC2S1rFmkkawgc_F-0-LSIQ,5807
|
23
|
+
cgc/commands/compute/compute_utills.py,sha256=oANUoJRt0L2VDKoMsAhEkE5lfigjRrX9fJjhWpG1Mc4,8615
|
24
24
|
cgc/commands/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
25
25
|
cgc/commands/db/db_cmd.py,sha256=zVcA5GHsW2sZ0Q826q7Va-4Y9iGmw-J7aBPwu-PWF8I,3293
|
26
26
|
cgc/commands/debug/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
27
27
|
cgc/commands/debug/debug_cmd.py,sha256=kuAuh5YOqzGFjoiYZwfM9FJ1z5OeSpC0JAIUEzS83lM,412
|
28
28
|
cgc/commands/resource/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
29
|
-
cgc/commands/resource/resource_cmd.py,sha256=
|
29
|
+
cgc/commands/resource/resource_cmd.py,sha256=nHIlUxtMJQnffy2TfcJy_N1Q6juMPsCK7gc542-fhmU,4021
|
30
30
|
cgc/commands/resource/resource_responses.py,sha256=sES7mAi_Cv5B6Z3I_6eUOqVwOr2HgMO45cz8MiNZetQ,197
|
31
31
|
cgc/commands/volume/__init__.py,sha256=Ou3kyb72aaXkrVCfQCVdniA65R2xHsRFgebooG1gflA,384
|
32
32
|
cgc/commands/volume/data_model.py,sha256=meprXdaXLo3mMTZta1ks1-BJ7G0rO16qi_ycH-sXJpY,1295
|
33
33
|
cgc/commands/volume/volume_cmd.py,sha256=C2K6PclAYhRm5optlmSxj4KujonYu8kdu1kGJQ0Nck4,7609
|
34
34
|
cgc/commands/volume/volume_responses.py,sha256=WefUohXxXZ9Znfc6P2XjoAM2RlA19hMKcVaW-xG9HWs,3199
|
35
35
|
cgc/commands/volume/volume_utils.py,sha256=n6s0FgpsYyxFMp_JdMRCzRi5Ar3_Svg9JDvWSdX4lhk,1919
|
36
|
-
cgc/sdk/__init__.py,sha256=
|
36
|
+
cgc/sdk/__init__.py,sha256=2KWwBgq1wxwrB1uTYfN4TvKoNAWiy5qugb1L7YPspfo,271
|
37
|
+
cgc/sdk/exceptions.py,sha256=99XIzDO6LYKjex715troH-MkGUN7hi2Bit4KHfSHDis,214
|
37
38
|
cgc/sdk/handlers.py,sha256=ECCHNe1pErsXFlmwHewsWRvYqzAZ5j5TrSqwernpLJk,868
|
38
39
|
cgc/sdk/mongodb.py,sha256=TJ2XU7nilNRXLOIpQQPrRiVxHN2TaVM5QOSuMRtNDVs,7221
|
39
40
|
cgc/sdk/postgresql.py,sha256=ziXaMMwjSF3k1OAID3F9npqWVxreQaoZ8wn7X8x1FZw,1637
|
40
41
|
cgc/sdk/redis.py,sha256=W5wS9Sgyv4098yzWAwG7qEk4HEDwscE3JmWgPC3NCzc,2844
|
42
|
+
cgc/sdk/resource.py,sha256=X1MqX2dYom5G8zu27onTdZR7tZK6-VlBgH9MXoQwfLY,13460
|
41
43
|
cgc/telemetry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
42
44
|
cgc/telemetry/basic.py,sha256=XagCcyH4QSEPmfiQ1WCjqXslnJO6IaJCY0AMPySd5rc,3175
|
43
45
|
cgc/tests/__init__.py,sha256=8aI3MVpkzaj0_UX02kZCtY5vmGO0rnq0mw2H04-OHf8,102743
|
@@ -53,23 +55,23 @@ cgc/tests/desired_responses/test_volume_list.txt,sha256=vYB1p50BBHD801q7LUdDc_ac
|
|
53
55
|
cgc/utils/__init__.py,sha256=l9JF-WnvmhlolxbDKlJPsxquZ-fjtvv7wKvn2zpu5IM,3466
|
54
56
|
cgc/utils/click_group.py,sha256=Scfw8eMIyt2dE1ezUq2JuiI-E_LklqXQXJEr7L-EG6A,633
|
55
57
|
cgc/utils/config_utils.py,sha256=VFTrcN9QeOByIobhh48TfAm-BWON4PN8zX0H8PdUNTU,2729
|
56
|
-
cgc/utils/custom_exceptions.py,sha256=
|
58
|
+
cgc/utils/custom_exceptions.py,sha256=V50fXz09c_VNzLkgVNCFihIN1o-lUqRV0XMpy8zTgH8,2068
|
57
59
|
cgc/utils/message_utils.py,sha256=jdUHtR2-gEvyxYu1T0OY-a6cBz5N8TatRaxKNuNcJtU,1766
|
58
60
|
cgc/utils/prepare_headers.py,sha256=FK04VFreStH6xcfgP8s5zmePilQPn0bKhCajgV0f2sY,2546
|
59
61
|
cgc/utils/requests_helper.py,sha256=ghn8LTxWqfRvy7BXQdxD4VHX8b-ypHkbnFXY05ig7_A,2050
|
60
|
-
cgc/utils/response_utils.py,sha256=
|
62
|
+
cgc/utils/response_utils.py,sha256=9vJqAt2UFJ1n-oesFPe6CB_ooGoStjl-twY_31Jt4_I,7374
|
61
63
|
cgc/utils/update.py,sha256=AsQwhcBqsjgNPKn6AN6ojt0Ew5otvJXyshys6bjr7DQ,413
|
62
64
|
cgc/utils/version_control.py,sha256=VsqNzNYWDvU3VsoPuYIaZV7Img-K2_DmEOHd7rZlRxk,3267
|
63
65
|
cgc/utils/consts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
64
|
-
cgc/utils/consts/env_consts.py,sha256=
|
66
|
+
cgc/utils/consts/env_consts.py,sha256=sHO1vzWZALiXB4Ej2E7S5v0i-DfTwHBIIC1fIP_MocI,1286
|
65
67
|
cgc/utils/consts/message_consts.py,sha256=8CIe3N_HL6Pj-gSArkPkpegsvm-QMWxqqnSgtzG08Qw,1218
|
66
68
|
cgc/utils/cryptography/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
67
69
|
cgc/utils/cryptography/aes_crypto.py,sha256=S0rKg38oy7rM5lTrP6DDpjLA-XRxuZggAXyxMFHtyzY,3333
|
68
70
|
cgc/utils/cryptography/encryption_module.py,sha256=rbblBBorHYPGl-iKblyZX3_NuPEvUTpnH1l_RgNGCbA,1958
|
69
71
|
cgc/utils/cryptography/rsa_crypto.py,sha256=h3jU5qPpj9uVjP1rTqZJTdYB5yjhD9HZpr_nD439h9Q,4180
|
70
|
-
cgcsdk-1.0.
|
71
|
-
cgcsdk-1.0.
|
72
|
-
cgcsdk-1.0.
|
73
|
-
cgcsdk-1.0.
|
74
|
-
cgcsdk-1.0.
|
75
|
-
cgcsdk-1.0.
|
72
|
+
cgcsdk-1.0.5.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
73
|
+
cgcsdk-1.0.5.dist-info/METADATA,sha256=rVxdoWqe-3QobxnPQ9LS40TLAAnVEkvUxkXDwM-5f98,1418
|
74
|
+
cgcsdk-1.0.5.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
75
|
+
cgcsdk-1.0.5.dist-info/entry_points.txt,sha256=bdfIHeJ6Y-BBr5yupCVoK7SUrJj1yNdew8OtIOg_3No,36
|
76
|
+
cgcsdk-1.0.5.dist-info/top_level.txt,sha256=nqW9tqcIcCXFigQT69AuOk7XHKc4pCuv4HGJQGXb6iA,12
|
77
|
+
cgcsdk-1.0.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|