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 CHANGED
@@ -5,5 +5,5 @@ API_SECURE_CONNECTION=yes
5
5
  CONFIG_FILE_NAME = cfg.json
6
6
  TMP_DIR = .tmp
7
7
  RELEASE = 1
8
- MAJOR_VERSION = 1
9
- MINOR_VERSION = 0
8
+ MAJOR_VERSION = 2
9
+ MINOR_VERSION = 1
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
@@ -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
- def api_keys_create(user_id: str, password: str):
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 user_id: user_id received in invite
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 for the user
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(user_id, password)
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)
@@ -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(user_id: str = None, password: str = None):
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
- save_to_config(api_key=api_key, api_secret=secret)
59
- if user_id is not None and password is not None:
60
- save_to_config(user_id=user_id, password=password)
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
- rent_start = cost["rent_start_cost"]
32
- rent_end = cost["rent_end_cost"]
33
- rent_time = f"{int(cost['rent_time_cost'])} s"
34
- rent_cost = f"{float(cost['cost_total']):.2f} pln"
35
- resources = cost["resources"]
36
- name = resources["name"]
37
- rent_type = cost["type"]
38
- if rent_type == "events_resource":
39
- entity = resources["entity"]
40
- elif rent_type == "events_volume":
41
- entity = "volume"
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
- entity = "ERROR"
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", "time", "cost"]
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
- pod["labels"].pop("app-name")
113
- pod["labels"].pop("entity")
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("pod_url")
122
- pod["labels"].pop("resource-type")
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
 
@@ -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
- if "pod-template-hash" in job["labels"].keys():
179
- job["labels"].pop("pod-template-hash")
180
- job["labels"].pop("entity")
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.0
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 >=2.11
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=U3C0HJQT97waXH8lYgrRl4JwcuH_0YpwS25TKm8U9xY,209
2
- cgc/CHANGELOG.md,sha256=A-OH4XghR7LI9eAVrdiphDuAEuhYVHi1k4HZ2fgFF-8,10253
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=f8_2oIlFQ1k0zvJXGfrwkdVoK5EJBwWesNdOcjtY_uM,4220
14
- cgc/commands/auth/auth_logic.py,sha256=PzEyC6OBRSne_5dQT-cq69J0i8BqfGL0MIoXW-yM8qE,2281
15
- cgc/commands/auth/auth_responses.py,sha256=kIGyCbNqSQKhDsVpIIcbzR-1bFQHkC3ClbUPBNfKPwk,2095
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=KNP-FRIeK20rBRoeyPCO5zKc90nXKQHH0pJ4-lZdNwY,9191
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=zXLbBBcWeOgur-r0OKiIjaKeaxMNxASXWzCTeTsyC6o,4711
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=pvTVEDqBazt6617sXsCMmlttCD6wZPpGQIrcrOd0Hyw,6707
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.0.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
89
- cgcsdk-1.1.0.dist-info/METADATA,sha256=OSfHEfaDvQhZ5y0tzORo9jwUKcOgsyf0FgyxyGVJAQc,2986
90
- cgcsdk-1.1.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
91
- cgcsdk-1.1.0.dist-info/entry_points.txt,sha256=bdfIHeJ6Y-BBr5yupCVoK7SUrJj1yNdew8OtIOg_3No,36
92
- cgcsdk-1.1.0.dist-info/top_level.txt,sha256=nqW9tqcIcCXFigQT69AuOk7XHKc4pCuv4HGJQGXb6iA,12
93
- cgcsdk-1.1.0.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.1.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5