cgcsdk 1.0.6__py3-none-any.whl → 1.0.7__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 +11 -0
- cgc/cgc.py +5 -1
- cgc/commands/cgc_cmd.py +28 -3
- cgc/commands/cgc_cmd_responses.py +8 -0
- cgc/commands/compute/compute_models.py +2 -0
- cgc/commands/compute/compute_utills.py +16 -6
- cgc/commands/jobs/__init__.py +10 -0
- cgc/commands/jobs/job_utils.py +175 -0
- cgc/commands/jobs/jobs_cmd.py +238 -0
- cgc/commands/jobs/jobs_responses.py +52 -0
- cgc/sdk/__init__.py +1 -0
- cgc/sdk/job.py +147 -0
- cgc/sdk/resource.py +1 -0
- cgc/utils/custom_exceptions.py +5 -1
- cgcsdk-1.0.7.dist-info/METADATA +70 -0
- {cgcsdk-1.0.6.dist-info → cgcsdk-1.0.7.dist-info}/RECORD +21 -16
- cgcsdk-1.0.6.dist-info/METADATA +0 -39
- {cgcsdk-1.0.6.dist-info → cgcsdk-1.0.7.dist-info}/LICENSE +0 -0
- {cgcsdk-1.0.6.dist-info → cgcsdk-1.0.7.dist-info}/WHEEL +0 -0
- {cgcsdk-1.0.6.dist-info → cgcsdk-1.0.7.dist-info}/entry_points.txt +0 -0
- {cgcsdk-1.0.6.dist-info → cgcsdk-1.0.7.dist-info}/top_level.txt +0 -0
cgc/.env
CHANGED
cgc/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 1.0.7
|
4
|
+
|
5
|
+
Release on April 04, 2024
|
6
|
+
|
7
|
+
* speed of resource creation with volumes / shared memory, has been improved
|
8
|
+
* added jobs creation: cgc job create
|
9
|
+
* added jobs list: cgc job list
|
10
|
+
* added jobs delete: cgc job delete
|
11
|
+
* added logs (STDOUT, STDERR) reader for spawned applications: cgc logs
|
12
|
+
* added SDK for logs & jobs
|
13
|
+
|
3
14
|
## 1.0.6
|
4
15
|
|
5
16
|
Release on March 13, 2024
|
cgc/cgc.py
CHANGED
@@ -4,6 +4,7 @@ from cgc.commands.compute.compute_cmd import compute_group
|
|
4
4
|
from cgc.commands.billing.billing_cmd import billing_group
|
5
5
|
from cgc.commands.db.db_cmd import db_group
|
6
6
|
from cgc.commands.resource.resource_cmd import resource_group
|
7
|
+
from cgc.commands.jobs.jobs_cmd import job_group
|
7
8
|
from cgc.commands.auth.auth_cmd import (
|
8
9
|
api_keys_group,
|
9
10
|
auth_register,
|
@@ -13,10 +14,12 @@ from cgc.commands.debug.debug_cmd import debug_group
|
|
13
14
|
from cgc.commands.cgc_cmd import (
|
14
15
|
cgc_rm,
|
15
16
|
cgc_status,
|
17
|
+
cgc_logs,
|
16
18
|
sending_telemetry_permission,
|
17
19
|
resource_events,
|
18
20
|
context_group,
|
19
21
|
)
|
22
|
+
|
20
23
|
from cgc.utils.version_control import check_version, _get_version
|
21
24
|
from cgc.utils.click_group import CustomGroup
|
22
25
|
|
@@ -41,7 +44,8 @@ cli.add_command(resource_group)
|
|
41
44
|
cli.add_command(billing_group)
|
42
45
|
cli.add_command(cgc_status)
|
43
46
|
cli.add_command(sending_telemetry_permission)
|
44
|
-
|
47
|
+
cli.add_command(cgc_logs)
|
48
|
+
cli.add_command(job_group)
|
45
49
|
|
46
50
|
if __name__ == "__main__" or __name__ == "cgc.cgc":
|
47
51
|
cli()
|
cgc/commands/cgc_cmd.py
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
import click
|
2
2
|
import json
|
3
3
|
|
4
|
-
from cgc.commands.cgc_cmd_responses import
|
4
|
+
from cgc.commands.cgc_cmd_responses import (
|
5
|
+
cgc_logs_response,
|
6
|
+
cgc_status_response,
|
7
|
+
)
|
5
8
|
from cgc.commands.resource.resource_cmd import resource_delete
|
6
9
|
from cgc.utils.requests_helper import call_api, EndpointTypes
|
7
10
|
from cgc.utils.click_group import CustomCommand, CustomGroup
|
@@ -9,7 +12,7 @@ from cgc.utils.prepare_headers import get_api_url_and_prepare_headers
|
|
9
12
|
from cgc.utils.response_utils import retrieve_and_validate_response_send_metric
|
10
13
|
from cgc.telemetry.basic import telemetry_permission_set
|
11
14
|
from cgc.commands.compute.compute_responses import compute_logs_response
|
12
|
-
from cgc.commands.auth.auth_cmd import auth_register
|
15
|
+
# from cgc.commands.auth.auth_cmd import auth_register
|
13
16
|
from cgc.utils import set_environment_data, check_if_config_exist, list_all_config_files
|
14
17
|
from cgc.commands.cgc_helpers import table_of_user_context_files
|
15
18
|
from cgc.utils.config_utils import config_path
|
@@ -37,7 +40,7 @@ def resource_events(app_name: str):
|
|
37
40
|
request=EndpointTypes.get,
|
38
41
|
url=url,
|
39
42
|
headers=headers,
|
40
|
-
data=json.dumps(__payload).encode(
|
43
|
+
data=json.dumps(__payload).encode("utf-8"),
|
41
44
|
)
|
42
45
|
click.echo(
|
43
46
|
compute_logs_response(retrieve_and_validate_response_send_metric(__res, metric))
|
@@ -122,3 +125,25 @@ def folder_of_contexts():
|
|
122
125
|
def get_env_path():
|
123
126
|
"""Displays current environment file path"""
|
124
127
|
click.echo(f"Current environment file path: {ENV_FILE_PATH}")
|
128
|
+
|
129
|
+
|
130
|
+
@click.command("logs", cls=CustomCommand)
|
131
|
+
@click.argument("app_name", type=click.STRING)
|
132
|
+
def cgc_logs(app_name):
|
133
|
+
"""Displays logs of a given app"""
|
134
|
+
|
135
|
+
if not app_name:
|
136
|
+
raise click.ClickException("Please provide a non-empty name")
|
137
|
+
|
138
|
+
api_url, headers = get_api_url_and_prepare_headers()
|
139
|
+
url = f"{api_url}/v1/api/resource/logs/{app_name}"
|
140
|
+
metric = "logs.get"
|
141
|
+
__res = call_api(
|
142
|
+
request=EndpointTypes.get,
|
143
|
+
url=url,
|
144
|
+
headers=headers,
|
145
|
+
)
|
146
|
+
|
147
|
+
click.echo(
|
148
|
+
cgc_logs_response(retrieve_and_validate_response_send_metric(__res, metric))
|
149
|
+
)
|
@@ -72,3 +72,11 @@ def cgc_status_response(data: dict):
|
|
72
72
|
),
|
73
73
|
headers=list_headers,
|
74
74
|
)
|
75
|
+
|
76
|
+
|
77
|
+
def cgc_logs_response(data: dict):
|
78
|
+
return "\n".join(
|
79
|
+
"==> %s/%s <==\n%s" % (pod, cont, log)
|
80
|
+
for pod, containers in data["details"]["logs"].items()
|
81
|
+
for cont, log in containers.items()
|
82
|
+
)
|
@@ -1,6 +1,6 @@
|
|
1
|
-
from ast import main
|
2
1
|
import cgc.utils.consts.env_consts as env_consts
|
3
2
|
|
3
|
+
|
4
4
|
def list_get_mounted_volumes_paths(volume_list: list) -> str:
|
5
5
|
"""Formats and returns list of PVC volumes mounted to an app.
|
6
6
|
|
@@ -20,6 +20,7 @@ def list_get_mounted_volumes_paths(volume_list: list) -> str:
|
|
20
20
|
)
|
21
21
|
return volumes_mounted
|
22
22
|
|
23
|
+
|
23
24
|
def list_get_mounted_volumes(volume_list: list) -> str:
|
24
25
|
"""Formats and returns list of PVC volumes mounted to an app.
|
25
26
|
|
@@ -39,16 +40,21 @@ def list_get_mounted_volumes(volume_list: list) -> str:
|
|
39
40
|
)
|
40
41
|
return volumes_mounted
|
41
42
|
|
42
|
-
|
43
|
+
|
44
|
+
def get_app_mounts(pod_list: list) -> list:
|
43
45
|
output_data = []
|
44
|
-
|
46
|
+
|
45
47
|
for pod in pod_list:
|
46
48
|
try:
|
47
|
-
main_container_name = pod["labels"]["entity"]
|
49
|
+
main_container_name = pod["labels"]["entity"]
|
48
50
|
try:
|
49
|
-
main_container = [
|
51
|
+
main_container = [
|
52
|
+
x for x in pod["containers"] if x["name"] == main_container_name
|
53
|
+
][0]
|
50
54
|
except IndexError:
|
51
|
-
raise Exception(
|
55
|
+
raise Exception(
|
56
|
+
"Parser was unable to find main container in server output in container list"
|
57
|
+
)
|
52
58
|
volumes_mounted = list_get_mounted_volumes(main_container["mounts"])
|
53
59
|
volumes_paths = list_get_mounted_volumes_paths(main_container["mounts"])
|
54
60
|
pod_data = {
|
@@ -63,6 +69,7 @@ def get_app_mounts(pod_list:list) -> list:
|
|
63
69
|
pass
|
64
70
|
return output_data
|
65
71
|
|
72
|
+
|
66
73
|
def get_app_list(pod_list: list, detailed: bool) -> list:
|
67
74
|
"""Formats and returns list of apps to print.
|
68
75
|
|
@@ -110,6 +117,9 @@ def get_app_list(pod_list: list, detailed: bool) -> list:
|
|
110
117
|
pod["labels"]["url"] = pod["labels"]["pod_url"]
|
111
118
|
pod["labels"].pop("app-token")
|
112
119
|
pod["labels"].pop("pod_url")
|
120
|
+
pod["labels"].pop("resource-type")
|
121
|
+
pod["labels"].pop("api-key-id", None)
|
122
|
+
pod["labels"].pop("user-id", None)
|
113
123
|
|
114
124
|
# appending the rest of labels
|
115
125
|
pod_data.update(pod["labels"])
|
@@ -0,0 +1,175 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
from cgc.commands.compute.compute_utills import list_get_mounted_volumes
|
3
|
+
import cgc.utils.consts.env_consts as env_consts
|
4
|
+
|
5
|
+
|
6
|
+
def job_delete_payload(name):
|
7
|
+
"""
|
8
|
+
Create payload for job delete.
|
9
|
+
"""
|
10
|
+
payload = {
|
11
|
+
"name": name,
|
12
|
+
}
|
13
|
+
return payload
|
14
|
+
|
15
|
+
|
16
|
+
def job_create_payload(
|
17
|
+
name,
|
18
|
+
cpu,
|
19
|
+
memory,
|
20
|
+
volumes: list,
|
21
|
+
volume_full_path: str,
|
22
|
+
resource_data: list = [],
|
23
|
+
config_maps_data: list = [],
|
24
|
+
gpu: int = 0,
|
25
|
+
gpu_type: str = None,
|
26
|
+
shm_size: int = 0,
|
27
|
+
image_name: str = "",
|
28
|
+
startup_command: str = "",
|
29
|
+
repository_secret: str = "",
|
30
|
+
ttl_seconds_after_finished: Optional[int] = None,
|
31
|
+
):
|
32
|
+
"""
|
33
|
+
Create payload for app creation.
|
34
|
+
"""
|
35
|
+
extra_payload = {}
|
36
|
+
if shm_size is not None and shm_size != 0:
|
37
|
+
extra_payload["shared_memory"] = shm_size
|
38
|
+
|
39
|
+
if ttl_seconds_after_finished is not None:
|
40
|
+
extra_payload["ttl_seconds_after_finished"] = ttl_seconds_after_finished
|
41
|
+
|
42
|
+
payload = {
|
43
|
+
"resource_data": {
|
44
|
+
"name": name,
|
45
|
+
"cpu": cpu,
|
46
|
+
"gpu": gpu,
|
47
|
+
"memory": memory,
|
48
|
+
"gpu_type": gpu_type,
|
49
|
+
"full_mount_path": volume_full_path,
|
50
|
+
**extra_payload,
|
51
|
+
}
|
52
|
+
}
|
53
|
+
try:
|
54
|
+
if len(volumes) != 0:
|
55
|
+
if not volume_full_path:
|
56
|
+
payload["resource_data"]["pv_volume"] = volumes
|
57
|
+
elif volume_full_path and len(volumes) != 1:
|
58
|
+
raise Exception(
|
59
|
+
"Volume full path can only be used with a single volume"
|
60
|
+
)
|
61
|
+
else:
|
62
|
+
payload["resource_data"]["pv_volume"] = volumes
|
63
|
+
except TypeError:
|
64
|
+
pass
|
65
|
+
try:
|
66
|
+
resource_data_dict = {"resource_data": {}}
|
67
|
+
if len(resource_data) != 0:
|
68
|
+
for resource in resource_data:
|
69
|
+
try:
|
70
|
+
key, value = resource.split("=")
|
71
|
+
resource_data_dict["resource_data"][key] = value
|
72
|
+
except ValueError:
|
73
|
+
raise Exception(
|
74
|
+
"Invalid resource data format. Use key=value format"
|
75
|
+
)
|
76
|
+
if image_name:
|
77
|
+
resource_data_dict["resource_data"]["custom_image"] = image_name
|
78
|
+
if startup_command:
|
79
|
+
resource_data_dict["resource_data"]["custom_command"] = startup_command
|
80
|
+
if repository_secret:
|
81
|
+
resource_data_dict["resource_data"][
|
82
|
+
"image_pull_secret_name"
|
83
|
+
] = repository_secret
|
84
|
+
if resource_data_dict["resource_data"] != {}:
|
85
|
+
payload["template_specific_data"] = resource_data_dict
|
86
|
+
except TypeError:
|
87
|
+
pass
|
88
|
+
try:
|
89
|
+
if len(config_maps_data) != 0:
|
90
|
+
config_maps_data_dict = {}
|
91
|
+
for config_map in config_maps_data:
|
92
|
+
try:
|
93
|
+
key, value = config_map.split(
|
94
|
+
"="
|
95
|
+
) # where key is name of config map and value is data
|
96
|
+
config_maps_data_dict[key] = (
|
97
|
+
value # value is dict, ex.: {"key": "value"}
|
98
|
+
)
|
99
|
+
except ValueError:
|
100
|
+
raise Exception(
|
101
|
+
"Invalid config map data format. Use key=value format"
|
102
|
+
)
|
103
|
+
payload["config_maps_data"] = config_maps_data_dict
|
104
|
+
except TypeError:
|
105
|
+
pass
|
106
|
+
return payload
|
107
|
+
|
108
|
+
|
109
|
+
def get_job_list(job_pod_list: list, job_list: list):
|
110
|
+
list_of_json_data = get_job_pod_list(job_pod_list)
|
111
|
+
for json_data in list_of_json_data:
|
112
|
+
for job in job_list:
|
113
|
+
if job.get("name") == json_data.get("name"):
|
114
|
+
json_data["ttl_seconds_after_finished"] = job.get(
|
115
|
+
"ttl_seconds_after_finished", "N/A"
|
116
|
+
)
|
117
|
+
break
|
118
|
+
return list_of_json_data
|
119
|
+
|
120
|
+
|
121
|
+
def get_job_pod_list(job_pod_list: list) -> list:
|
122
|
+
"""Formats and returns list of jobs to print.
|
123
|
+
|
124
|
+
:param pod_list: list of pods
|
125
|
+
:type pod_list: list
|
126
|
+
:return: formatted list of apps
|
127
|
+
:rtype: list
|
128
|
+
"""
|
129
|
+
output_data = []
|
130
|
+
|
131
|
+
for pod in job_pod_list:
|
132
|
+
try:
|
133
|
+
main_container_name = "custom-job"
|
134
|
+
try:
|
135
|
+
main_container = [
|
136
|
+
x
|
137
|
+
for x in pod.get("containers", [])
|
138
|
+
if x.get("name") == main_container_name
|
139
|
+
][0]
|
140
|
+
except IndexError:
|
141
|
+
raise Exception(
|
142
|
+
"Parser was unable to find main container in server output in container list"
|
143
|
+
)
|
144
|
+
volumes_mounted = list_get_mounted_volumes(main_container.get("mounts", []))
|
145
|
+
limits = main_container.get("resources", {}).get("limits")
|
146
|
+
cpu = limits.get("cpu") if limits is not None else 0
|
147
|
+
ram = limits.get("memory") if limits is not None else "0Gi"
|
148
|
+
|
149
|
+
pod_data = {
|
150
|
+
"name": pod.get("labels", {}).get("app-name"),
|
151
|
+
"status": pod.get("status", {}),
|
152
|
+
"volumes_mounted": volumes_mounted,
|
153
|
+
"cpu": cpu,
|
154
|
+
"ram": ram,
|
155
|
+
"gpu-count": pod.get("labels", {}).get("gpu-count", 0),
|
156
|
+
"gpu-label": pod.get("labels", {}).get("gpu-label", "N/A"),
|
157
|
+
}
|
158
|
+
# getting rid of unwanted and used values
|
159
|
+
if "pod-template-hash" in pod["labels"].keys():
|
160
|
+
pod["labels"].pop("pod-template-hash")
|
161
|
+
pod["labels"].pop("app-name")
|
162
|
+
pod["labels"].pop("entity")
|
163
|
+
pod["labels"].pop("resource-type")
|
164
|
+
pod["labels"].pop("job-name")
|
165
|
+
pod["labels"].pop("controller-uid")
|
166
|
+
pod["labels"].pop("api-key-id", None)
|
167
|
+
pod["labels"].pop("user-id", None)
|
168
|
+
|
169
|
+
# appending the rest of labels
|
170
|
+
pod_data.update(pod["labels"])
|
171
|
+
output_data.append(pod_data)
|
172
|
+
except KeyError:
|
173
|
+
pass
|
174
|
+
|
175
|
+
return output_data
|
@@ -0,0 +1,238 @@
|
|
1
|
+
import click
|
2
|
+
import json
|
3
|
+
import sys
|
4
|
+
|
5
|
+
from cgc.commands.jobs.job_utils import job_delete_payload, job_create_payload
|
6
|
+
from cgc.commands.jobs.jobs_responses import (
|
7
|
+
job_delete_response,
|
8
|
+
job_list_response,
|
9
|
+
job_create_response,
|
10
|
+
)
|
11
|
+
from cgc.commands.compute.compute_models import GPUsList
|
12
|
+
from cgc.utils.prepare_headers import get_api_url_and_prepare_headers
|
13
|
+
from cgc.utils.response_utils import (
|
14
|
+
retrieve_and_validate_response_send_metric,
|
15
|
+
)
|
16
|
+
from cgc.utils.click_group import CustomGroup, CustomCommand
|
17
|
+
from cgc.utils.requests_helper import call_api, EndpointTypes
|
18
|
+
|
19
|
+
|
20
|
+
@click.group(name="job", cls=CustomGroup, hidden=False)
|
21
|
+
def job_group():
|
22
|
+
"""
|
23
|
+
Management of jobs.
|
24
|
+
"""
|
25
|
+
|
26
|
+
|
27
|
+
@job_group.command("delete", cls=CustomCommand)
|
28
|
+
@click.argument("name", type=click.STRING)
|
29
|
+
def job_delete(name: str):
|
30
|
+
"""
|
31
|
+
Delete an job using backend endpoint.
|
32
|
+
\f
|
33
|
+
:param name: name of job to delete
|
34
|
+
:type name: str
|
35
|
+
"""
|
36
|
+
api_url, headers = get_api_url_and_prepare_headers()
|
37
|
+
url = f"{api_url}/v1/api/job/delete"
|
38
|
+
metric = "job.delete"
|
39
|
+
__payload = job_delete_payload(name=name)
|
40
|
+
__res = call_api(
|
41
|
+
request=EndpointTypes.delete,
|
42
|
+
url=url,
|
43
|
+
headers=headers,
|
44
|
+
data=json.dumps(__payload).encode("utf-8"),
|
45
|
+
)
|
46
|
+
click.echo(
|
47
|
+
job_delete_response(retrieve_and_validate_response_send_metric(__res, metric))
|
48
|
+
)
|
49
|
+
|
50
|
+
|
51
|
+
@job_group.command("list", cls=CustomCommand)
|
52
|
+
def job_list():
|
53
|
+
"""List all ports for a running resource"""
|
54
|
+
api_url, headers = get_api_url_and_prepare_headers()
|
55
|
+
url = f"{api_url}/v1/api/job/list"
|
56
|
+
metric = "job.list"
|
57
|
+
__res = call_api(
|
58
|
+
request=EndpointTypes.get,
|
59
|
+
url=url,
|
60
|
+
headers=headers,
|
61
|
+
)
|
62
|
+
click.echo(
|
63
|
+
job_list_response(retrieve_and_validate_response_send_metric(__res, metric))
|
64
|
+
)
|
65
|
+
|
66
|
+
|
67
|
+
@job_group.command("create", cls=CustomCommand)
|
68
|
+
@click.argument("startup_command", required=False)
|
69
|
+
@click.option(
|
70
|
+
"-n", "--name", "name", type=click.STRING, required=True, help="Desired app name"
|
71
|
+
)
|
72
|
+
@click.option(
|
73
|
+
"-g",
|
74
|
+
"--gpu",
|
75
|
+
"gpu",
|
76
|
+
type=click.INT,
|
77
|
+
default=0,
|
78
|
+
help="How much GPU cards app will use",
|
79
|
+
)
|
80
|
+
@click.option(
|
81
|
+
"-gt",
|
82
|
+
"--gpu-type",
|
83
|
+
"gpu_type",
|
84
|
+
type=click.Choice(GPUsList.get_list(), case_sensitive=False),
|
85
|
+
default="A5000",
|
86
|
+
help="Graphic card used by the app",
|
87
|
+
)
|
88
|
+
@click.option(
|
89
|
+
"-c",
|
90
|
+
"--cpu",
|
91
|
+
"cpu",
|
92
|
+
type=click.INT,
|
93
|
+
default=1,
|
94
|
+
help="How much CPU cores app can use",
|
95
|
+
)
|
96
|
+
@click.option(
|
97
|
+
"-m",
|
98
|
+
"--memory",
|
99
|
+
"memory",
|
100
|
+
type=click.INT,
|
101
|
+
default=2,
|
102
|
+
help="How much Gi RAM app can use",
|
103
|
+
)
|
104
|
+
@click.option(
|
105
|
+
"-v",
|
106
|
+
"--volume",
|
107
|
+
"volumes",
|
108
|
+
multiple=True,
|
109
|
+
help="List of volume names to be mounted with default mount path",
|
110
|
+
)
|
111
|
+
@click.option(
|
112
|
+
"-fp",
|
113
|
+
"--full-path",
|
114
|
+
"volume_full_path",
|
115
|
+
type=click.STRING,
|
116
|
+
help="If set, full path will be used for volume mount. Valid for 1 volume.",
|
117
|
+
)
|
118
|
+
@click.option(
|
119
|
+
"-d",
|
120
|
+
"--resource-data",
|
121
|
+
"resource_data",
|
122
|
+
multiple=True,
|
123
|
+
help="List of optional arguments to be passed to the app, key=value format",
|
124
|
+
)
|
125
|
+
@click.option(
|
126
|
+
"--image",
|
127
|
+
"image_name",
|
128
|
+
type=click.STRING,
|
129
|
+
help="Image to be used by the app",
|
130
|
+
)
|
131
|
+
@click.option(
|
132
|
+
"--repository-secret",
|
133
|
+
"repository_secret",
|
134
|
+
type=click.STRING,
|
135
|
+
help="Use secret to pull image from private repository",
|
136
|
+
)
|
137
|
+
@click.option(
|
138
|
+
"-cm",
|
139
|
+
"--config-map",
|
140
|
+
"config_maps_data",
|
141
|
+
multiple=True,
|
142
|
+
help="List of optional arguments to be passed to the app, key=value format",
|
143
|
+
)
|
144
|
+
@click.option(
|
145
|
+
"--shm",
|
146
|
+
"shm_size",
|
147
|
+
type=click.IntRange(0, 1024, clamp=True),
|
148
|
+
default=0,
|
149
|
+
help="Size of shared memory in Gi",
|
150
|
+
)
|
151
|
+
@click.option(
|
152
|
+
"--ttl",
|
153
|
+
"ttl_seconds_after_finished",
|
154
|
+
type=click.INT,
|
155
|
+
default=None,
|
156
|
+
help="Time to live in seconds after app is finished",
|
157
|
+
)
|
158
|
+
def job_create(
|
159
|
+
gpu: int,
|
160
|
+
gpu_type: str,
|
161
|
+
cpu: int,
|
162
|
+
memory: int,
|
163
|
+
volumes: list[str],
|
164
|
+
volume_full_path: str,
|
165
|
+
resource_data: list[str],
|
166
|
+
config_maps_data: list[str],
|
167
|
+
name: str,
|
168
|
+
shm_size: int,
|
169
|
+
image_name: str,
|
170
|
+
startup_command: str,
|
171
|
+
repository_secret: str,
|
172
|
+
ttl_seconds_after_finished: int,
|
173
|
+
):
|
174
|
+
"""
|
175
|
+
Create job in user namespace.
|
176
|
+
\f
|
177
|
+
:param gpu: number of gpus to be used by app
|
178
|
+
:type gpu: int
|
179
|
+
:param cpu: number of cores to be used by app
|
180
|
+
:type cpu: int
|
181
|
+
:param memory: GB of memory to be used by app
|
182
|
+
:type memory: int
|
183
|
+
:param volumes: list of volumes to mount
|
184
|
+
:type volumes: list[str]
|
185
|
+
:param volume_full_path: if set, full path will be used for volume mount
|
186
|
+
:type volume_full_path: str
|
187
|
+
:param resource_data: list of optional arguments to be passed to the app
|
188
|
+
:type resource_data: list[str]
|
189
|
+
:param config_maps_data: list of optional arguments to be passed to the app
|
190
|
+
:type config_maps_data: list[str]
|
191
|
+
:param name: name of app
|
192
|
+
:type name: str
|
193
|
+
:param shm_size: size of shared memory
|
194
|
+
:type shm_size: int
|
195
|
+
:param image_name: name of image to be used by the app
|
196
|
+
:type image_name: str
|
197
|
+
:param startup_command: command to be executed on app startup; it is stdin input
|
198
|
+
:type startup_command: str
|
199
|
+
:param repository_secret: use secret to pull image from private repository
|
200
|
+
:type repository_secret: str
|
201
|
+
:param ttl_seconds_after_finished: time to live in seconds after app is finished
|
202
|
+
:type ttl_seconds_after_finished: int
|
203
|
+
"""
|
204
|
+
api_url, headers = get_api_url_and_prepare_headers()
|
205
|
+
url = f"{api_url}/v1/api/job/create"
|
206
|
+
cleaned_data = ""
|
207
|
+
if not sys.stdin.isatty():
|
208
|
+
input_data = sys.stdin.read()
|
209
|
+
cleaned_data = input_data.replace("|", "")
|
210
|
+
startup_command = cleaned_data
|
211
|
+
elif startup_command:
|
212
|
+
cleaned_data = startup_command
|
213
|
+
metric = "job.create"
|
214
|
+
__payload = job_create_payload(
|
215
|
+
name=name,
|
216
|
+
cpu=cpu,
|
217
|
+
memory=memory,
|
218
|
+
gpu=gpu,
|
219
|
+
volumes=volumes,
|
220
|
+
volume_full_path=volume_full_path,
|
221
|
+
resource_data=resource_data,
|
222
|
+
config_maps_data=config_maps_data,
|
223
|
+
gpu_type=gpu_type,
|
224
|
+
shm_size=shm_size,
|
225
|
+
image_name=image_name,
|
226
|
+
startup_command=cleaned_data,
|
227
|
+
repository_secret=repository_secret,
|
228
|
+
ttl_seconds_after_finished=ttl_seconds_after_finished,
|
229
|
+
)
|
230
|
+
__res = call_api(
|
231
|
+
request=EndpointTypes.post,
|
232
|
+
url=url,
|
233
|
+
headers=headers,
|
234
|
+
data=json.dumps(__payload).encode("utf-8"),
|
235
|
+
)
|
236
|
+
click.echo(
|
237
|
+
job_create_response(retrieve_and_validate_response_send_metric(__res, metric))
|
238
|
+
)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
from cgc.commands.jobs import NoJobsToList
|
2
|
+
from cgc.commands.jobs.job_utils import get_job_list
|
3
|
+
from cgc.telemetry.basic import change_gauge, setup_gauge
|
4
|
+
from cgc.utils.config_utils import get_namespace
|
5
|
+
from cgc.utils.message_utils import key_error_decorator_for_helpers
|
6
|
+
from cgc.utils.response_utils import (
|
7
|
+
fill_missing_values_in_a_response,
|
8
|
+
tabulate_a_response,
|
9
|
+
)
|
10
|
+
|
11
|
+
|
12
|
+
@key_error_decorator_for_helpers
|
13
|
+
def job_delete_response(data: dict) -> str:
|
14
|
+
"""Create response string for job delete command.
|
15
|
+
|
16
|
+
:param response: dict object from API response.
|
17
|
+
:type response: requests.Response
|
18
|
+
:return: Response string.
|
19
|
+
:rtype: str
|
20
|
+
"""
|
21
|
+
name = data.get("details", {}).get("job_deleted", {}).get("name")
|
22
|
+
change_gauge(f"{get_namespace()}.job.count", -1)
|
23
|
+
return f"Job {name} and its service successfully deleted."
|
24
|
+
|
25
|
+
|
26
|
+
@key_error_decorator_for_helpers
|
27
|
+
def job_list_response(data: dict) -> list:
|
28
|
+
job_pod_list = data.get("details", {}).get("job_pod_list", [])
|
29
|
+
job_list = data.get("details", {}).get("job_list", [])
|
30
|
+
setup_gauge(f"{get_namespace()}.job.count", len(job_list))
|
31
|
+
|
32
|
+
if not job_list:
|
33
|
+
raise NoJobsToList()
|
34
|
+
|
35
|
+
list_of_json_data = get_job_list(job_pod_list, job_list)
|
36
|
+
table = fill_missing_values_in_a_response(list_of_json_data)
|
37
|
+
|
38
|
+
return tabulate_a_response(table)
|
39
|
+
|
40
|
+
|
41
|
+
@key_error_decorator_for_helpers
|
42
|
+
def job_create_response(data: dict) -> str:
|
43
|
+
"""Create response string for job create command.
|
44
|
+
|
45
|
+
:param response: dict object from API response.
|
46
|
+
:type response: requests.Response
|
47
|
+
:return: Response string.
|
48
|
+
:rtype: str
|
49
|
+
"""
|
50
|
+
name = data.get("details", {}).get("job_created", {}).get("name")
|
51
|
+
change_gauge(f"{get_namespace()}.job.count", 1)
|
52
|
+
return f"Job {name} created successfully."
|
cgc/sdk/__init__.py
CHANGED
cgc/sdk/job.py
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
import json as _json
|
2
|
+
from typing import Optional
|
3
|
+
import cgc.sdk.exceptions as _exceptions
|
4
|
+
from cgc.utils.custom_exceptions import CUSTOM_EXCEPTIONS
|
5
|
+
import cgc.utils.prepare_headers as _prepare_headers
|
6
|
+
import cgc.commands.jobs.job_utils as _job_utils
|
7
|
+
import cgc.utils.requests_helper as _requests_helper
|
8
|
+
import cgc.utils.response_utils as _response_utils
|
9
|
+
from cgc.commands.compute.compute_models import GPUsList
|
10
|
+
|
11
|
+
|
12
|
+
def job_create(
|
13
|
+
name: str,
|
14
|
+
image_name: str,
|
15
|
+
cpu: int = 1,
|
16
|
+
memory: int = 2,
|
17
|
+
shm_size: int = 0,
|
18
|
+
gpu: int = 0,
|
19
|
+
gpu_type: str = "A5000",
|
20
|
+
volumes: list = [],
|
21
|
+
volume_full_path: str = "",
|
22
|
+
startup_command: str = "",
|
23
|
+
repository_secret: str = "",
|
24
|
+
resource_data: list = [],
|
25
|
+
config_maps_data: list = [],
|
26
|
+
ttl_seconds_after_finished: Optional[int] = None,
|
27
|
+
):
|
28
|
+
"""
|
29
|
+
Create a custom compute resource.
|
30
|
+
|
31
|
+
:param name: The name of the compute resource.
|
32
|
+
:type name: str
|
33
|
+
:param image_name: The name of the image to use for the compute resource.
|
34
|
+
:type image_name: str,
|
35
|
+
:param cpu: The number of CPUs for the compute resource, defaults to 1.
|
36
|
+
:type cpu: int, optional
|
37
|
+
:param memory: The amount of memory (in GB) for the compute resource, defaults to 2.
|
38
|
+
:type memory: int, optional
|
39
|
+
:param shm_size: The size of the shared memory (in GB) for the compute resource, defaults to 0.
|
40
|
+
:type shm_size: int, optional
|
41
|
+
:param gpu: The number of GPUs for the compute resource, defaults to 0.
|
42
|
+
:type gpu: int, optional
|
43
|
+
:param gpu_type: The type of GPU for the compute resource, defaults to "A5000".
|
44
|
+
:type gpu_type: str, optional
|
45
|
+
:param volumes: The list of volumes to attach to the compute resource, defaults to [].
|
46
|
+
:type volumes: list, optional
|
47
|
+
:param volume_full_path: The full path of the volume, defaults to "".
|
48
|
+
:type volume_full_path: str, optional
|
49
|
+
:param startup_command: The startup command for the compute resource, defaults to "".
|
50
|
+
:type startup_command: str, optional
|
51
|
+
:param repository_secret: The secret for accessing the repository, defaults to "".
|
52
|
+
:type repository_secret: str, optional
|
53
|
+
:param resource_data: The additional resource data, defaults to [].
|
54
|
+
:type resource_data: list, optional
|
55
|
+
:param config_maps_data: The additional config maps data, defaults to [].
|
56
|
+
:type config_maps_data: list, optional
|
57
|
+
:param ttl_seconds_after_finished: The time to live in seconds after the app is finished, defaults to None.
|
58
|
+
:type ttl_seconds_after_finished: int, optional
|
59
|
+
:raises _SDKException: If the image name is not provided.
|
60
|
+
:raises _SDKException: If an invalid GPU type is specified.
|
61
|
+
:return: The response from the API call.
|
62
|
+
:rtype: _type_
|
63
|
+
"""
|
64
|
+
if not image_name:
|
65
|
+
raise _exceptions.SDKException(-2, "Image name is required")
|
66
|
+
api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
|
67
|
+
url = f"{api_url}/v1/api/job/create"
|
68
|
+
metric = "job.create"
|
69
|
+
gpu_type = gpu_type.upper()
|
70
|
+
if gpu_type not in GPUsList.get_list():
|
71
|
+
raise _exceptions.SDKException(-3, f"Invalid GPU type: {gpu_type}")
|
72
|
+
__payload = _job_utils.job_create_payload(
|
73
|
+
name=name,
|
74
|
+
cpu=cpu,
|
75
|
+
memory=memory,
|
76
|
+
gpu=gpu,
|
77
|
+
gpu_type=gpu_type,
|
78
|
+
volumes=volumes,
|
79
|
+
volume_full_path=volume_full_path,
|
80
|
+
resource_data=resource_data,
|
81
|
+
config_maps_data=config_maps_data,
|
82
|
+
shm_size=shm_size,
|
83
|
+
image_name=image_name,
|
84
|
+
startup_command=startup_command,
|
85
|
+
repository_secret=repository_secret,
|
86
|
+
ttl_seconds_after_finished=ttl_seconds_after_finished,
|
87
|
+
)
|
88
|
+
|
89
|
+
__res = _requests_helper.call_api(
|
90
|
+
request=_requests_helper.EndpointTypes.post,
|
91
|
+
url=url,
|
92
|
+
headers=headers,
|
93
|
+
data=_json.dumps(__payload).encode("utf-8"),
|
94
|
+
)
|
95
|
+
|
96
|
+
return _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
|
97
|
+
__res, metric
|
98
|
+
)
|
99
|
+
|
100
|
+
|
101
|
+
def job_list():
|
102
|
+
"""
|
103
|
+
List jobs using backend endpoint.
|
104
|
+
|
105
|
+
:return: response from the API call
|
106
|
+
:rtype: dict
|
107
|
+
"""
|
108
|
+
api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
|
109
|
+
url = f"{api_url}/v1/api/job/list"
|
110
|
+
metric = "job.list"
|
111
|
+
__res = _requests_helper.call_api(
|
112
|
+
request=_requests_helper.EndpointTypes.get,
|
113
|
+
url=url,
|
114
|
+
headers=headers,
|
115
|
+
)
|
116
|
+
|
117
|
+
return _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
|
118
|
+
__res, metric
|
119
|
+
)
|
120
|
+
# job_pod_list = _response.get("details", {}).get("job_pod_list", [])
|
121
|
+
# job_list = _response.get("details", {}).get("job_list", [])
|
122
|
+
# return _job_utils.get_job_list(job_pod_list, job_list)
|
123
|
+
|
124
|
+
|
125
|
+
def job_delete(name: str):
|
126
|
+
"""
|
127
|
+
Delete job using backend endpoint.
|
128
|
+
|
129
|
+
:param name: name of job to delete
|
130
|
+
:type name: str
|
131
|
+
:return: response from the API call
|
132
|
+
:rtype: dict
|
133
|
+
"""
|
134
|
+
api_url, headers = _prepare_headers.get_api_url_and_prepare_headers()
|
135
|
+
url = f"{api_url}/v1/api/job/delete"
|
136
|
+
metric = "job.delete"
|
137
|
+
__payload = _job_utils.job_delete_payload(name=name)
|
138
|
+
__res = _requests_helper.call_api(
|
139
|
+
request=_requests_helper.EndpointTypes.delete,
|
140
|
+
url=url,
|
141
|
+
headers=headers,
|
142
|
+
data=_json.dumps(__payload).encode("utf-8"),
|
143
|
+
)
|
144
|
+
|
145
|
+
return _response_utils.retrieve_and_validate_response_send_metric_for_sdk(
|
146
|
+
__res, metric
|
147
|
+
)
|
cgc/sdk/resource.py
CHANGED
cgc/utils/custom_exceptions.py
CHANGED
@@ -15,7 +15,8 @@ CUSTOM_EXCEPTIONS = {
|
|
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_PORTS_ALREADY_EXISTS": "Port with this name already exists.",
|
18
|
-
"RESOURCE_TEMPLATE_NAME_ALREADY_EXISTS": "
|
18
|
+
"RESOURCE_TEMPLATE_NAME_ALREADY_EXISTS": "Resource with this name already exists.",
|
19
|
+
"JOB_CREATE_ALREADY_EXISTS": "Job with this name already exists.",
|
19
20
|
},
|
20
21
|
404: {
|
21
22
|
"PVC_CREATE_NO_SC": "Selected disk type and access mode unavailable",
|
@@ -30,11 +31,14 @@ CUSTOM_EXCEPTIONS = {
|
|
30
31
|
"COMPUTE_CREATE_TEMPLATE_NOT_FOUND": "There is no template with this name.",
|
31
32
|
"COMPUTE_TEMPLATE_NAME_NOT_FOUND": "No app with this name.",
|
32
33
|
"COMPUTE_RESOURCE_QUOTA_NOT_FOUND": "You do not have enforced limits on your namespace.",
|
34
|
+
"JOB_NOT_FOUND": "Job with this name not found.",
|
35
|
+
"RESOURCE_NOT_FOUND": "Resource with this name not found.",
|
33
36
|
},
|
34
37
|
400: {
|
35
38
|
"WRONG_DATE_FORMAT": "Wrong date format.",
|
36
39
|
"ENTITY_NOT_ALLOWED": "You can't create this entity.",
|
37
40
|
"PVC_MOUNT_ALREADY_MOUNTED": "This volume is already mounted.",
|
38
41
|
"TEMPLATE_NAME_SYSTEM_RESERVED": "You can't create app with this name.",
|
42
|
+
"JOB_LACKS_REQUIRED_PARAMETER": "Job requires container image parameter.",
|
39
43
|
},
|
40
44
|
}
|
@@ -0,0 +1,70 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: cgcsdk
|
3
|
+
Version: 1.0.7
|
4
|
+
Summary: Comtegra GPU Cloud REST API client
|
5
|
+
Author: Comtegra AI Team
|
6
|
+
Author-email: ai@comtegra.pl
|
7
|
+
License: BSD 2-clause
|
8
|
+
Project-URL: Documentation, https://example.com/documentation/
|
9
|
+
Project-URL: GitHub, https://github.com/foobar/foobar/
|
10
|
+
Project-URL: Changelog, https://github.com/foobar/foobar/blob/master/CHANGELOG.md
|
11
|
+
Keywords: cloud,sdk,orchestrator,kubernetes,jupyter-notebook
|
12
|
+
Classifier: Development Status :: 1 - Planning
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
14
|
+
Classifier: License :: OSI Approved :: BSD License
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
17
|
+
Classifier: Programming Language :: Python :: 3.4
|
18
|
+
Classifier: Programming Language :: Python :: 3.5
|
19
|
+
Classifier: Programming Language :: Python :: 3.8
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
22
|
+
Description-Content-Type: text/markdown
|
23
|
+
License-File: LICENSE
|
24
|
+
Requires-Dist: click
|
25
|
+
Requires-Dist: python-dotenv
|
26
|
+
Requires-Dist: tabulate
|
27
|
+
Requires-Dist: pycryptodomex
|
28
|
+
Requires-Dist: paramiko >=2.11
|
29
|
+
Requires-Dist: statsd
|
30
|
+
Requires-Dist: requests
|
31
|
+
Requires-Dist: setuptools
|
32
|
+
Requires-Dist: colorama
|
33
|
+
Requires-Dist: redis
|
34
|
+
Requires-Dist: pymongo
|
35
|
+
Requires-Dist: psycopg2-binary
|
36
|
+
|
37
|
+
# Comtegra GPU Cloud CLI Client
|
38
|
+
|
39
|
+
## Basic info
|
40
|
+
|
41
|
+
CGC Clinet is complete solution to create and manage your compute resources through CLI interface and python code. It incorporates CLI and SDK in one package.
|
42
|
+
|
43
|
+
CGC CLI is a command line interface for Comtegra GPU Cloud. CGC CLI enables management of your Comtegra GPU Cloud resources. Current version of the app provides support for compute, storage and network resurces to be created, listed and deleted. Every compute resource is given to you as an URL, which is accessible from open Internet.
|
44
|
+
|
45
|
+
To enable better access to your storage resources, every account has the ability to spawn free of charge filebrowser which is local implementation of dropbox. Remember to mount newely created volumes to it.
|
46
|
+
|
47
|
+
For now, we provide the ability to spawn compute resources like:
|
48
|
+
|
49
|
+
1. [Jupyter notebook](https://jupyter.org/) with tensorflow or pytorch installed as default
|
50
|
+
2. [Triton inferencing server](https://docs.nvidia.com/deeplearning/triton-inference-server/) for large scale inferencing
|
51
|
+
3. [Label studio](https://labelstud.io/) for easy management of your data annotation tasks with variety of modes
|
52
|
+
4. [Rapids](https://rapids.ai/) suite of accelerated libraries for data processing
|
53
|
+
|
54
|
+
Notebooks are equiped with all CUDA libraries and GPU drivers which enables the usage of GPU for accelerated computations.
|
55
|
+
Apart from compute resources, we provide the database engines accessible from within your namespace:
|
56
|
+
|
57
|
+
1. [MongoDB](https://www.mongodb.org/)
|
58
|
+
2. [PostgreSQL](https://www.postgresql.org/)
|
59
|
+
3. [Redis](https://redis.io/)
|
60
|
+
4. [Weaviate](https://weaviate.io/)
|
61
|
+
|
62
|
+
More are coming!
|
63
|
+
Please follow instructions to get started.
|
64
|
+
|
65
|
+
## More info
|
66
|
+
|
67
|
+
If you'd like to know more visit:
|
68
|
+
|
69
|
+
- [Comtegra GPU Website](https://cgc.comtegra.cloud)
|
70
|
+
- [Docs](https://docs.cgc.comtegra.cloud)
|
@@ -1,11 +1,11 @@
|
|
1
|
-
cgc/.env,sha256=
|
2
|
-
cgc/CHANGELOG.md,sha256=
|
1
|
+
cgc/.env,sha256=0RLfGys6OqABCJ5Ak-tj_AUbvaLN1A6Erd4O-fnAUXE,209
|
2
|
+
cgc/CHANGELOG.md,sha256=unRU_finGpvnpbv2U4hFa1E_IJ_g0X6uoGW4jTmZ3P8,7808
|
3
3
|
cgc/__init__.py,sha256=d03Xv8Pw4ktNyUHfmicP6XfxYPXnVYLaCZPyUlg_RNQ,326
|
4
|
-
cgc/cgc.py,sha256=
|
4
|
+
cgc/cgc.py,sha256=wcngqkKT1AxkKcc-F63cBrfx9mxLKfDM8H8ciuIFnyw,1493
|
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=
|
8
|
-
cgc/commands/cgc_cmd_responses.py,sha256=
|
7
|
+
cgc/commands/cgc_cmd.py,sha256=QwqjZl9FmAMOvuUQMJm-wOV88OxbdwK5UIS0zkg-mAo,4956
|
8
|
+
cgc/commands/cgc_cmd_responses.py,sha256=wO9Hf5_4jaw1ZRhQvh9Za0S_vhc22t-0_CoEGz5ndNE,2245
|
9
9
|
cgc/commands/cgc_helpers.py,sha256=ngArFjVw-8P_2g7J8k3b9xgDxfJw7JeaOtkhTySMSGU,1174
|
10
10
|
cgc/commands/exceptions.py,sha256=l3Sms3D2fxSpLQQQEYeLWxO3to82myTQ0VFgFYdQdLU,45
|
11
11
|
cgc/commands/auth/__init__.py,sha256=K8HkHHotMnK7SQRAst5rx_wprHEphPo_w2KToEymjAY,399
|
@@ -18,13 +18,17 @@ cgc/commands/billing/billing_responses.py,sha256=HAD5N-Odx3Jz1OmhO4v66rHoXpTYIOG
|
|
18
18
|
cgc/commands/billing/billing_utils.py,sha256=zXLbBBcWeOgur-r0OKiIjaKeaxMNxASXWzCTeTsyC6o,4711
|
19
19
|
cgc/commands/compute/__init__.py,sha256=lCdLfZ0ECSHtXEUSwq5YRHH85yXHchSsz8ZJvmClPtI,239
|
20
20
|
cgc/commands/compute/compute_cmd.py,sha256=jgO61ULXIAuRn1LcFn0VU0X2-oLvBQB-0_OYMzXau0w,15490
|
21
|
-
cgc/commands/compute/compute_models.py,sha256=
|
21
|
+
cgc/commands/compute/compute_models.py,sha256=Am9UdlTGZRGIEOw4oHvFHwedM1IMunOTCVuhZ8nMiRc,1173
|
22
22
|
cgc/commands/compute/compute_responses.py,sha256=eOmcllyOqPYqN0kSUzSpuC2S1rFmkkawgc_F-0-LSIQ,5807
|
23
|
-
cgc/commands/compute/compute_utills.py,sha256=
|
23
|
+
cgc/commands/compute/compute_utills.py,sha256=XXnd_EuF9vCmW14r0ThlN98ScKSx3KAeqFbWy2g2mlk,8812
|
24
24
|
cgc/commands/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
25
25
|
cgc/commands/db/db_cmd.py,sha256=r0iwQ9oIQLcozshVyaHBxQMijYLrcfKvm0bXr9jGhek,3309
|
26
26
|
cgc/commands/debug/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
27
27
|
cgc/commands/debug/debug_cmd.py,sha256=kuAuh5YOqzGFjoiYZwfM9FJ1z5OeSpC0JAIUEzS83lM,412
|
28
|
+
cgc/commands/jobs/__init__.py,sha256=E-438wgIzlnGmXs5jgmWAhJ1KNV6UXF2gz8SXu3UxA0,231
|
29
|
+
cgc/commands/jobs/job_utils.py,sha256=BMwd7GA-WxC-NfpiziCQkG1TCNP98S3DWNpYmJbFCLY,5952
|
30
|
+
cgc/commands/jobs/jobs_cmd.py,sha256=Q-orK6B9Zk1zAf8sOM6QqF9Eeu092P-UEg4GRA-zX-s,6555
|
31
|
+
cgc/commands/jobs/jobs_responses.py,sha256=XYFTWHKvHKeZ-aOWlO0MI0iZnVm77SKqg9Y3N2CJaZU,1771
|
28
32
|
cgc/commands/resource/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
29
33
|
cgc/commands/resource/resource_cmd.py,sha256=ALROaJPRK5BB8R_oEC7JzOl3J8umMmhC_HJuwGFaw_M,4053
|
30
34
|
cgc/commands/resource/resource_responses.py,sha256=sES7mAi_Cv5B6Z3I_6eUOqVwOr2HgMO45cz8MiNZetQ,197
|
@@ -33,13 +37,14 @@ cgc/commands/volume/data_model.py,sha256=meprXdaXLo3mMTZta1ks1-BJ7G0rO16qi_ycH-s
|
|
33
37
|
cgc/commands/volume/volume_cmd.py,sha256=5bd0vq5--Y7IfniO33tGBYH0Z67Le2s_AMTiwJcPBA4,7673
|
34
38
|
cgc/commands/volume/volume_responses.py,sha256=WefUohXxXZ9Znfc6P2XjoAM2RlA19hMKcVaW-xG9HWs,3199
|
35
39
|
cgc/commands/volume/volume_utils.py,sha256=n6s0FgpsYyxFMp_JdMRCzRi5Ar3_Svg9JDvWSdX4lhk,1919
|
36
|
-
cgc/sdk/__init__.py,sha256=
|
40
|
+
cgc/sdk/__init__.py,sha256=qR4-ySTsunbZBRqtUGa6DAzW-E8fXf1wlDSwIifLOeU,297
|
37
41
|
cgc/sdk/exceptions.py,sha256=99XIzDO6LYKjex715troH-MkGUN7hi2Bit4KHfSHDis,214
|
38
42
|
cgc/sdk/handlers.py,sha256=ECCHNe1pErsXFlmwHewsWRvYqzAZ5j5TrSqwernpLJk,868
|
43
|
+
cgc/sdk/job.py,sha256=SclaqhY0rDACT_yg9e9AiYTsmXglIt6iTharwUPSAa4,5332
|
39
44
|
cgc/sdk/mongodb.py,sha256=TJ2XU7nilNRXLOIpQQPrRiVxHN2TaVM5QOSuMRtNDVs,7221
|
40
45
|
cgc/sdk/postgresql.py,sha256=ziXaMMwjSF3k1OAID3F9npqWVxreQaoZ8wn7X8x1FZw,1637
|
41
46
|
cgc/sdk/redis.py,sha256=W5wS9Sgyv4098yzWAwG7qEk4HEDwscE3JmWgPC3NCzc,2844
|
42
|
-
cgc/sdk/resource.py,sha256=
|
47
|
+
cgc/sdk/resource.py,sha256=Wodt8pe15zg1uuclfV4Qxntffph70ULhNig_gOgMKz8,13544
|
43
48
|
cgc/telemetry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
44
49
|
cgc/telemetry/basic.py,sha256=XagCcyH4QSEPmfiQ1WCjqXslnJO6IaJCY0AMPySd5rc,3175
|
45
50
|
cgc/tests/__init__.py,sha256=8aI3MVpkzaj0_UX02kZCtY5vmGO0rnq0mw2H04-OHf8,102743
|
@@ -55,7 +60,7 @@ cgc/tests/desired_responses/test_volume_list.txt,sha256=vYB1p50BBHD801q7LUdDc_ac
|
|
55
60
|
cgc/utils/__init__.py,sha256=l9JF-WnvmhlolxbDKlJPsxquZ-fjtvv7wKvn2zpu5IM,3466
|
56
61
|
cgc/utils/click_group.py,sha256=Scfw8eMIyt2dE1ezUq2JuiI-E_LklqXQXJEr7L-EG6A,633
|
57
62
|
cgc/utils/config_utils.py,sha256=VFTrcN9QeOByIobhh48TfAm-BWON4PN8zX0H8PdUNTU,2729
|
58
|
-
cgc/utils/custom_exceptions.py,sha256=
|
63
|
+
cgc/utils/custom_exceptions.py,sha256=eVduUpAwUqSCi1hyqal36xj-kWVcy9AeGdhbs1praYI,2432
|
59
64
|
cgc/utils/message_utils.py,sha256=jdUHtR2-gEvyxYu1T0OY-a6cBz5N8TatRaxKNuNcJtU,1766
|
60
65
|
cgc/utils/prepare_headers.py,sha256=xNDins83jdMre80s3orsR3Xu0jPUd82CHppvLq2tbeA,2561
|
61
66
|
cgc/utils/requests_helper.py,sha256=ghn8LTxWqfRvy7BXQdxD4VHX8b-ypHkbnFXY05ig7_A,2050
|
@@ -69,9 +74,9 @@ cgc/utils/cryptography/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
|
|
69
74
|
cgc/utils/cryptography/aes_crypto.py,sha256=S0rKg38oy7rM5lTrP6DDpjLA-XRxuZggAXyxMFHtyzY,3333
|
70
75
|
cgc/utils/cryptography/encryption_module.py,sha256=rbblBBorHYPGl-iKblyZX3_NuPEvUTpnH1l_RgNGCbA,1958
|
71
76
|
cgc/utils/cryptography/rsa_crypto.py,sha256=h3jU5qPpj9uVjP1rTqZJTdYB5yjhD9HZpr_nD439h9Q,4180
|
72
|
-
cgcsdk-1.0.
|
73
|
-
cgcsdk-1.0.
|
74
|
-
cgcsdk-1.0.
|
75
|
-
cgcsdk-1.0.
|
76
|
-
cgcsdk-1.0.
|
77
|
-
cgcsdk-1.0.
|
77
|
+
cgcsdk-1.0.7.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
78
|
+
cgcsdk-1.0.7.dist-info/METADATA,sha256=lJihZEso4JIomyx6n8X1CA5GHAQT9B3XCWrFR1dANjw,3174
|
79
|
+
cgcsdk-1.0.7.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
80
|
+
cgcsdk-1.0.7.dist-info/entry_points.txt,sha256=bdfIHeJ6Y-BBr5yupCVoK7SUrJj1yNdew8OtIOg_3No,36
|
81
|
+
cgcsdk-1.0.7.dist-info/top_level.txt,sha256=nqW9tqcIcCXFigQT69AuOk7XHKc4pCuv4HGJQGXb6iA,12
|
82
|
+
cgcsdk-1.0.7.dist-info/RECORD,,
|
cgcsdk-1.0.6.dist-info/METADATA
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: cgcsdk
|
3
|
-
Version: 1.0.6
|
4
|
-
Summary: Comtegra GPU Cloud REST API client
|
5
|
-
Author: Comtegra AI Team
|
6
|
-
Author-email: ai@comtegra.pl
|
7
|
-
License: BSD 2-clause
|
8
|
-
Project-URL: Documentation, https://example.com/documentation/
|
9
|
-
Project-URL: GitHub, https://github.com/foobar/foobar/
|
10
|
-
Project-URL: Changelog, https://github.com/foobar/foobar/blob/master/CHANGELOG.md
|
11
|
-
Keywords: cloud,sdk,orchestrator,kubernetes,jupyter-notebook
|
12
|
-
Classifier: Development Status :: 1 - Planning
|
13
|
-
Classifier: Intended Audience :: Science/Research
|
14
|
-
Classifier: License :: OSI Approved :: BSD License
|
15
|
-
Classifier: Operating System :: POSIX :: Linux
|
16
|
-
Classifier: Programming Language :: Python :: 3
|
17
|
-
Classifier: Programming Language :: Python :: 3.4
|
18
|
-
Classifier: Programming Language :: Python :: 3.5
|
19
|
-
Classifier: Programming Language :: Python :: 3.8
|
20
|
-
Classifier: Programming Language :: Python :: 3.9
|
21
|
-
Classifier: Programming Language :: Python :: 3.10
|
22
|
-
Description-Content-Type: text/markdown
|
23
|
-
License-File: LICENSE
|
24
|
-
Requires-Dist: click
|
25
|
-
Requires-Dist: python-dotenv
|
26
|
-
Requires-Dist: tabulate
|
27
|
-
Requires-Dist: pycryptodomex
|
28
|
-
Requires-Dist: paramiko >=2.11
|
29
|
-
Requires-Dist: statsd
|
30
|
-
Requires-Dist: requests
|
31
|
-
Requires-Dist: setuptools
|
32
|
-
Requires-Dist: colorama
|
33
|
-
Requires-Dist: redis
|
34
|
-
Requires-Dist: pymongo
|
35
|
-
Requires-Dist: psycopg2-binary
|
36
|
-
|
37
|
-
# CGC Client - K8s Cloud
|
38
|
-
|
39
|
-
cgc command line client for k8s Comtegra cloud environment.
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|