cgcsdk 1.1.0__py3-none-any.whl → 1.2.1__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 +2 -2
- cgc/CHANGELOG.md +18 -0
- cgc/commands/auth/auth_cmd.py +48 -4
- cgc/commands/auth/auth_logic.py +21 -4
- cgc/commands/auth/auth_responses.py +1 -1
- cgc/commands/compute/billing/billing_utils.py +18 -15
- cgc/commands/compute/compute_utils.py +17 -4
- cgc/commands/jobs/job_utils.py +14 -3
- {cgcsdk-1.1.0.dist-info → cgcsdk-1.2.1.dist-info}/METADATA +2 -2
- {cgcsdk-1.1.0.dist-info → cgcsdk-1.2.1.dist-info}/RECORD +14 -14
- {cgcsdk-1.1.0.dist-info → cgcsdk-1.2.1.dist-info}/WHEEL +1 -1
- {cgcsdk-1.1.0.dist-info → cgcsdk-1.2.1.dist-info}/LICENSE +0 -0
- {cgcsdk-1.1.0.dist-info → cgcsdk-1.2.1.dist-info}/entry_points.txt +0 -0
- {cgcsdk-1.1.0.dist-info → cgcsdk-1.2.1.dist-info}/top_level.txt +0 -0
cgc/.env
CHANGED
cgc/CHANGELOG.md
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 1.2.1
|
4
|
+
|
5
|
+
Release on Nov 26, 2024
|
6
|
+
|
7
|
+
* `cgc register` now correctly saves api-keys into user configuration file
|
8
|
+
|
9
|
+
## 1.2.0
|
10
|
+
|
11
|
+
Release on Nov 20, 2024
|
12
|
+
|
13
|
+
* `cgc job list` now process more information from the job payload data
|
14
|
+
* processing resource labels in the `list` endpoints
|
15
|
+
* `cgc api-keys create` allows managing API keys levels (CGC, LLM)
|
16
|
+
* `cgc api-keys list` now shows more information about the API keys
|
17
|
+
* support for one-off billing events
|
18
|
+
* `gpu-label` and `gpu-count` retrieval from jobs
|
19
|
+
* `cgc api-keys create` does not overwrite existing keys by default
|
20
|
+
|
3
21
|
## 1.1.0
|
4
22
|
|
5
23
|
Release on Oct 02, 2024
|
cgc/commands/auth/auth_cmd.py
CHANGED
@@ -90,6 +90,21 @@ def auth_register(config_filename: str = "cfg.json"):
|
|
90
90
|
|
91
91
|
|
92
92
|
@api_keys_group.command("create", cls=CustomCommand)
|
93
|
+
@click.option(
|
94
|
+
"--level",
|
95
|
+
"-l",
|
96
|
+
"level",
|
97
|
+
type=click.STRING,
|
98
|
+
required=False,
|
99
|
+
help="Level of API key",
|
100
|
+
)
|
101
|
+
@click.option(
|
102
|
+
"--expires-in",
|
103
|
+
"-ei",
|
104
|
+
"expires_in",
|
105
|
+
type=click.INT,
|
106
|
+
required=False,
|
107
|
+
)
|
93
108
|
@click.option(
|
94
109
|
"--user",
|
95
110
|
"-u",
|
@@ -104,18 +119,47 @@ def auth_register(config_filename: str = "cfg.json"):
|
|
104
119
|
type=click.STRING,
|
105
120
|
required=False,
|
106
121
|
)
|
107
|
-
|
122
|
+
@click.option(
|
123
|
+
"--overwrite",
|
124
|
+
"-o",
|
125
|
+
"overwrite",
|
126
|
+
type=click.BOOL,
|
127
|
+
required=False,
|
128
|
+
default=False,
|
129
|
+
help="Overwrite existing API key locally",
|
130
|
+
)
|
131
|
+
@click.option(
|
132
|
+
"--comment",
|
133
|
+
"-c",
|
134
|
+
"comment",
|
135
|
+
type=click.STRING,
|
136
|
+
required=False,
|
137
|
+
)
|
138
|
+
def api_keys_create(
|
139
|
+
level: str, expires_in: int, user_id: str, password: str, overwrite: bool,
|
140
|
+
comment: str
|
141
|
+
):
|
108
142
|
"""Login a user in system using user id and password, then creates new API key pair and overwrites existing.
|
109
143
|
\f
|
110
|
-
:param
|
144
|
+
:param level: level of API key
|
145
|
+
:type level: str
|
146
|
+
:param expires_in: expiration time of API key
|
147
|
+
:type expires_in: int
|
148
|
+
:param user_id: username
|
111
149
|
:type user_id: str
|
112
|
-
:param password: password
|
150
|
+
:param password: password
|
113
151
|
:type password: str
|
152
|
+
:param overwrite: overwrite existing API key
|
153
|
+
:type overwrite: bool
|
114
154
|
"""
|
115
|
-
api_key, secret = auth_create_api_key_with_save(
|
155
|
+
api_key, secret = auth_create_api_key_with_save(
|
156
|
+
user_id, password, level, expires_in, overwrite, comment
|
157
|
+
)
|
116
158
|
click.echo(login_successful_response())
|
117
159
|
click.echo(f"API key: {api_key}")
|
118
160
|
click.echo(f"API secret: {secret}")
|
161
|
+
if comment:
|
162
|
+
click.echo(f"Comment: {comment}")
|
119
163
|
|
120
164
|
|
121
165
|
@api_keys_group.command("delete", cls=CustomCommand)
|
cgc/commands/auth/auth_logic.py
CHANGED
@@ -41,9 +41,25 @@ def auth_list_api_keys(user_id: str = None, password: str = None):
|
|
41
41
|
return json_data["details"].get("api_keys")
|
42
42
|
|
43
43
|
|
44
|
-
def auth_create_api_key_with_save(
|
44
|
+
def auth_create_api_key_with_save(
|
45
|
+
user_id: str = None,
|
46
|
+
password: str = None,
|
47
|
+
level: str = None,
|
48
|
+
expires_in: int = None,
|
49
|
+
overwrite: bool = False,
|
50
|
+
comment: str = None,
|
51
|
+
):
|
45
52
|
"""Function to create API key and API secret for user and save it to config file."""
|
46
53
|
url = f"{get_headers_data.load_user_api_url()}/v1/api/user/create/api-key"
|
54
|
+
query_params = []
|
55
|
+
if level is not None:
|
56
|
+
query_params.append(f"level={level}")
|
57
|
+
if expires_in is not None:
|
58
|
+
query_params.append(f"expires_in={expires_in}")
|
59
|
+
if comment is not None:
|
60
|
+
query_params.append(f"comment={comment}")
|
61
|
+
if query_params:
|
62
|
+
url += "?" + "&".join(query_params)
|
47
63
|
headers = prepare_headers.prepare_headers_api_key(user_id, password)
|
48
64
|
metric = "auth.api-key"
|
49
65
|
__res = call_api(
|
@@ -55,7 +71,8 @@ def auth_create_api_key_with_save(user_id: str = None, password: str = None):
|
|
55
71
|
json_data = retrieve_and_validate_response_send_metric(__res, metric)
|
56
72
|
api_key = json_data["details"].get("_id")
|
57
73
|
secret = json_data["details"].get("secret")
|
58
|
-
|
59
|
-
|
60
|
-
|
74
|
+
if overwrite:
|
75
|
+
save_to_config(api_key=api_key, api_secret=secret)
|
76
|
+
if user_id is not None and password is not None:
|
77
|
+
save_to_config(user_id=user_id, password=password)
|
61
78
|
return api_key, secret
|
@@ -31,7 +31,7 @@ def auth_register_response(
|
|
31
31
|
cgc_api_url=cgc_api_url,
|
32
32
|
cgc_secret=cgc_secret,
|
33
33
|
)
|
34
|
-
auth_logic.auth_create_api_key_with_save()
|
34
|
+
auth_logic.auth_create_api_key_with_save(overwrite=True)
|
35
35
|
shutil.rmtree(TMP_DIR_PATH)
|
36
36
|
# cfg.json
|
37
37
|
if config_filename == "cfg.json":
|
@@ -28,22 +28,25 @@ def _get_costs_list_for_user(costs_list: list):
|
|
28
28
|
user_costs_list_to_print = []
|
29
29
|
total_user_cost = 0
|
30
30
|
for cost in costs_list:
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
31
|
+
if cost["type"] == "events_resource":
|
32
|
+
resources = cost["resources"]
|
33
|
+
user_costs_list_to_print.append([resources["entity"],
|
34
|
+
resources["name"], cost["rent_start_cost"],
|
35
|
+
cost["rent_end_cost"], f"{int(cost['rent_time_cost'])} s",
|
36
|
+
f"{float(cost['cost_total']):.2f} pln"])
|
37
|
+
elif cost["type"] == "events_volume":
|
38
|
+
resources = cost["resources"]
|
39
|
+
user_costs_list_to_print.append(["volume", resources["name"],
|
40
|
+
cost["rent_start_cost"], cost["rent_end_cost"],
|
41
|
+
f"{int(cost['rent_time_cost'])} s",
|
42
|
+
f"{float(cost['cost_total']):.2f} pln"])
|
43
|
+
elif cost["type"] == "events_oneoff":
|
44
|
+
user_costs_list_to_print.append(["oneoff", cost["product"],
|
45
|
+
cost["date"], "-", cost["quantity"],
|
46
|
+
f"{float(cost['cost_total']):.8f} pln"])
|
42
47
|
else:
|
43
|
-
|
48
|
+
user_costs_list_to_print.append(["ERROR", "", "", "", "", ""])
|
44
49
|
total_user_cost += float(cost["cost_total"])
|
45
|
-
row_list = [entity, name, rent_start, rent_end, rent_time, rent_cost]
|
46
|
-
user_costs_list_to_print.append(row_list)
|
47
50
|
if costs_list:
|
48
51
|
user_costs_list_to_print.sort(key=lambda d: f"{d[0]} {d[2]}")
|
49
52
|
return user_costs_list_to_print, total_user_cost
|
@@ -89,7 +92,7 @@ def _get_status_list_headers():
|
|
89
92
|
:return: list of headers
|
90
93
|
:rtype: list
|
91
94
|
"""
|
92
|
-
return ["entity", "name", "start", "end", "
|
95
|
+
return ["entity", "name", "start", "end", "quantity", "cost"]
|
93
96
|
|
94
97
|
|
95
98
|
# TODO: refactor to use: tabulate_a_response(data: list) -> str:
|
@@ -109,8 +109,17 @@ def get_app_list(pod_list: list, detailed: bool) -> list:
|
|
109
109
|
# getting rid of unwanted and used values
|
110
110
|
if "pod-template-hash" in pod["labels"].keys():
|
111
111
|
pod["labels"].pop("pod-template-hash")
|
112
|
-
|
113
|
-
|
112
|
+
# getting rid of unwanted and used values
|
113
|
+
unwanted_labels = [
|
114
|
+
"app-name",
|
115
|
+
"resource-type",
|
116
|
+
"pod-template-hash",
|
117
|
+
"entity",
|
118
|
+
]
|
119
|
+
for unwanted_label in unwanted_labels:
|
120
|
+
if unwanted_label in pod["labels"].keys():
|
121
|
+
pod["labels"].pop(unwanted_label, None)
|
122
|
+
|
114
123
|
if detailed:
|
115
124
|
pod["labels"]["url"] = pod["labels"]["pod_url"]
|
116
125
|
if pod_data["type"] == "nvidia-pytorch":
|
@@ -118,8 +127,12 @@ def get_app_list(pod_list: list, detailed: bool) -> list:
|
|
118
127
|
else:
|
119
128
|
pod["labels"]["url"] = pod["labels"]["pod_url"]
|
120
129
|
pod["labels"].pop("app-token", None)
|
121
|
-
pod["labels"].pop(
|
122
|
-
|
130
|
+
pod["labels"].pop(
|
131
|
+
"pod_url", None
|
132
|
+
) # should be always present in the payload
|
133
|
+
pod["labels"].pop(
|
134
|
+
"resource-type", None
|
135
|
+
) # should be always present in the payload
|
123
136
|
pod["labels"].pop("api-key-id", None)
|
124
137
|
pod["labels"].pop("user-id", None)
|
125
138
|
|
cgc/commands/jobs/job_utils.py
CHANGED
@@ -166,6 +166,8 @@ def get_job_json_data(job_list: list):
|
|
166
166
|
limits = main_container.get("resources", {}).get("limits")
|
167
167
|
cpu = limits.get("cpu") if limits is not None else 0
|
168
168
|
ram = limits.get("memory") if limits is not None else "0Gi"
|
169
|
+
gpu_label = job.get("labels", {}).get("gpu-type", "N/A")
|
170
|
+
gpu_count = job.get("labels", {}).get("gpu-count", "N/A")
|
169
171
|
|
170
172
|
job_data = {
|
171
173
|
"name": job.get("labels", {}).get("app-name"),
|
@@ -173,11 +175,20 @@ def get_job_json_data(job_list: list):
|
|
173
175
|
"volumes_mounted": volumes_mounted,
|
174
176
|
"cpu": cpu,
|
175
177
|
"ram": ram,
|
178
|
+
"gpu-label": gpu_label,
|
179
|
+
"gpu-count": gpu_count,
|
176
180
|
}
|
177
181
|
# getting rid of unwanted and used values
|
178
|
-
|
179
|
-
|
180
|
-
|
182
|
+
unwanted_labels = [
|
183
|
+
"app-name",
|
184
|
+
"api-key-id",
|
185
|
+
"resource-type",
|
186
|
+
"pod-template-hash",
|
187
|
+
"entity",
|
188
|
+
]
|
189
|
+
for unwanted_label in unwanted_labels:
|
190
|
+
if unwanted_label in job["labels"].keys():
|
191
|
+
job["labels"].pop(unwanted_label)
|
181
192
|
|
182
193
|
# appending the rest of labels
|
183
194
|
job_data.update(job["labels"])
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: cgcsdk
|
3
|
-
Version: 1.1
|
3
|
+
Version: 1.2.1
|
4
4
|
Summary: CGC Core REST API client
|
5
5
|
Home-page: https://cgc.comtegra.cloud/
|
6
6
|
Author: Comtegra AI Team
|
@@ -23,7 +23,7 @@ Requires-Dist: click
|
|
23
23
|
Requires-Dist: python-dotenv
|
24
24
|
Requires-Dist: tabulate
|
25
25
|
Requires-Dist: pycryptodomex
|
26
|
-
Requires-Dist: paramiko
|
26
|
+
Requires-Dist: paramiko>=2.11
|
27
27
|
Requires-Dist: statsd
|
28
28
|
Requires-Dist: requests
|
29
29
|
Requires-Dist: setuptools
|
@@ -1,5 +1,5 @@
|
|
1
|
-
cgc/.env,sha256=
|
2
|
-
cgc/CHANGELOG.md,sha256=
|
1
|
+
cgc/.env,sha256=gURax26_tsuDMuNv--5GXRS582fTfqeKIzfS_68MgHo,209
|
2
|
+
cgc/CHANGELOG.md,sha256=zA7_E9mg39i9-lYf2c-BGdpvBkoPvnKISIFDAbDWIxQ,10815
|
3
3
|
cgc/__init__.py,sha256=d03Xv8Pw4ktNyUHfmicP6XfxYPXnVYLaCZPyUlg_RNQ,326
|
4
4
|
cgc/cgc.py,sha256=3I_Ef0ggX9caaJKJkhfGYSe8XwkHzSWxwGAClMHDnUs,1663
|
5
5
|
cgc/config.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -10,26 +10,26 @@ cgc/commands/cgc_helpers.py,sha256=-RduMrEDknGx-bxLRUX413JY9nuqWxBIGcwbwWrnDb4,1
|
|
10
10
|
cgc/commands/cgc_models.py,sha256=ThYrflL6gfiu3u3cWCbiisY7vfkKbo9JGqd5Jqr2jXc,352
|
11
11
|
cgc/commands/exceptions.py,sha256=kVCWEdcqmkLO5QVIINdEXQw29_glyX2C11ZMFgT7b8E,155
|
12
12
|
cgc/commands/auth/__init__.py,sha256=JnqNezSEUEVVPQIymya4llXr8LIug2vymQSAGBr4Ovg,397
|
13
|
-
cgc/commands/auth/auth_cmd.py,sha256=
|
14
|
-
cgc/commands/auth/auth_logic.py,sha256=
|
15
|
-
cgc/commands/auth/auth_responses.py,sha256=
|
13
|
+
cgc/commands/auth/auth_cmd.py,sha256=7sG1MWh1vbC9_lGFOLVRXGyXEnIbvF49mtA5EOehfJE,5082
|
14
|
+
cgc/commands/auth/auth_logic.py,sha256=2XSVbPdxcHbWSQKUHgwz8JPw11_hiyDJFFTkYOAjIC4,2751
|
15
|
+
cgc/commands/auth/auth_responses.py,sha256=OlhZnE5yvS1EPmkVfOeCg7wBq5ayW7o_fAzN0OCAfgY,2109
|
16
16
|
cgc/commands/auth/auth_utils.py,sha256=3RSBAR_V5DANhJ_R0cN4poP2uCNdx2tU1VXxP17yXig,3605
|
17
17
|
cgc/commands/compute/__init__.py,sha256=lCdLfZ0ECSHtXEUSwq5YRHH85yXHchSsz8ZJvmClPtI,239
|
18
18
|
cgc/commands/compute/compute_cmd.py,sha256=YZYR0milioX8aShLUC5NSKWcJSimQx6d4WmweAgdCrw,16131
|
19
19
|
cgc/commands/compute/compute_models.py,sha256=MNwxG94fFJfaQ3kGlVXSJh1H5qPHLSU_6BnoiuWS7UA,860
|
20
20
|
cgc/commands/compute/compute_responses.py,sha256=X1ExOKDIxSFZbBRbWyfWSCeRAe3_cFGa3jOgq6CfjHw,6439
|
21
|
-
cgc/commands/compute/compute_utils.py,sha256=
|
21
|
+
cgc/commands/compute/compute_utils.py,sha256=RrbswO8GKQjocAK4RiMz3YqckftTsqwR5uTvIt3kevo,9662
|
22
22
|
cgc/commands/compute/billing/__init__.py,sha256=ccjz-AzBCROjuR11qZRM4_62slI9ErmLi27xPUoRPHM,752
|
23
23
|
cgc/commands/compute/billing/billing_cmd.py,sha256=8lGf2GPyr09ZmqkRshmgfSPQgXa9o_IDSfwZgMfBE4k,4156
|
24
24
|
cgc/commands/compute/billing/billing_responses.py,sha256=5vQSR_d41uizengzfXlHXL7XivO_73PpWdKmoUgqYNw,1965
|
25
|
-
cgc/commands/compute/billing/billing_utils.py,sha256=
|
25
|
+
cgc/commands/compute/billing/billing_utils.py,sha256=Tiaka_glvNcrBN_2AE2iDFMkAim8tZ5sMzbaHRXNV4A,5081
|
26
26
|
cgc/commands/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
27
27
|
cgc/commands/db/db_cmd.py,sha256=cUS5t91QL_VfLaXJITYUjBiszoE-P8PYnm3u8VkmHEQ,3750
|
28
28
|
cgc/commands/db/db_models.py,sha256=zpMcrDBanLx7YQJ_-PWrQCbh3B-C0qWn1Pt5SnDwsh0,1351
|
29
29
|
cgc/commands/debug/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
30
30
|
cgc/commands/debug/debug_cmd.py,sha256=kuAuh5YOqzGFjoiYZwfM9FJ1z5OeSpC0JAIUEzS83lM,412
|
31
31
|
cgc/commands/jobs/__init__.py,sha256=E-438wgIzlnGmXs5jgmWAhJ1KNV6UXF2gz8SXu3UxA0,231
|
32
|
-
cgc/commands/jobs/job_utils.py,sha256
|
32
|
+
cgc/commands/jobs/job_utils.py,sha256=-r5YNt0Wr_LRL2bOp9RJ3OKSej827UYew2ds4qFy5_U,7135
|
33
33
|
cgc/commands/jobs/jobs_cmd.py,sha256=4zHZtT2y_FoBrGNR5nfSJ0gi-MX1rUP68KwpvuZ8m0Q,6922
|
34
34
|
cgc/commands/jobs/jobs_responses.py,sha256=QXFXA4zwQOo5Gvq5rEc7J_cxxsYqkdU19X9MCcZetUM,1771
|
35
35
|
cgc/commands/resource/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -85,9 +85,9 @@ cgc/utils/cryptography/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
|
|
85
85
|
cgc/utils/cryptography/aes_crypto.py,sha256=S0rKg38oy7rM5lTrP6DDpjLA-XRxuZggAXyxMFHtyzY,3333
|
86
86
|
cgc/utils/cryptography/encryption_module.py,sha256=rbblBBorHYPGl-iKblyZX3_NuPEvUTpnH1l_RgNGCbA,1958
|
87
87
|
cgc/utils/cryptography/rsa_crypto.py,sha256=h3jU5qPpj9uVjP1rTqZJTdYB5yjhD9HZpr_nD439h9Q,4180
|
88
|
-
cgcsdk-1.1.
|
89
|
-
cgcsdk-1.1.
|
90
|
-
cgcsdk-1.1.
|
91
|
-
cgcsdk-1.1.
|
92
|
-
cgcsdk-1.1.
|
93
|
-
cgcsdk-1.1.
|
88
|
+
cgcsdk-1.2.1.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
89
|
+
cgcsdk-1.2.1.dist-info/METADATA,sha256=b0Tli_BWBGVBNhq7WDZ7h97cZH_lt4sDG9Mtk-_GYH8,2985
|
90
|
+
cgcsdk-1.2.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
91
|
+
cgcsdk-1.2.1.dist-info/entry_points.txt,sha256=bdfIHeJ6Y-BBr5yupCVoK7SUrJj1yNdew8OtIOg_3No,36
|
92
|
+
cgcsdk-1.2.1.dist-info/top_level.txt,sha256=nqW9tqcIcCXFigQT69AuOk7XHKc4pCuv4HGJQGXb6iA,12
|
93
|
+
cgcsdk-1.2.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|