cgcsdk 1.0.15__py3-none-any.whl → 1.0.16__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 +15 -0
- cgc/commands/auth/auth_responses.py +10 -4
- cgc/commands/cgc_cmd.py +3 -1
- cgc/commands/cgc_cmd_responses.py +75 -48
- cgc/commands/cgc_models.py +11 -3
- cgc/commands/compute/compute_cmd.py +40 -6
- cgc/commands/compute/compute_models.py +17 -19
- cgc/commands/db/db_cmd.py +9 -0
- cgc/commands/db/db_models.py +12 -10
- cgc/commands/jobs/job_utils.py +13 -6
- cgc/commands/jobs/jobs_cmd.py +11 -0
- cgc/commands/volume/data_model.py +2 -3
- cgc/commands/volume/volume_cmd.py +34 -19
- cgc/commands/volume/volume_models.py +22 -0
- cgc/commands/volume/volume_responses.py +27 -1
- cgc/telemetry/basic.py +1 -1
- cgc/utils/config_utils.py +106 -4
- cgc/utils/consts/env_consts.py +0 -4
- cgc/utils/consts/message_consts.py +1 -1
- cgc/utils/version_control.py +23 -6
- {cgcsdk-1.0.15.dist-info → cgcsdk-1.0.16.dist-info}/METADATA +1 -1
- {cgcsdk-1.0.15.dist-info → cgcsdk-1.0.16.dist-info}/RECORD +27 -26
- {cgcsdk-1.0.15.dist-info → cgcsdk-1.0.16.dist-info}/WHEEL +1 -1
- {cgcsdk-1.0.15.dist-info → cgcsdk-1.0.16.dist-info}/LICENSE +0 -0
- {cgcsdk-1.0.15.dist-info → cgcsdk-1.0.16.dist-info}/entry_points.txt +0 -0
- {cgcsdk-1.0.15.dist-info → cgcsdk-1.0.16.dist-info}/top_level.txt +0 -0
cgc/.env
CHANGED
cgc/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 1.0.16
|
4
|
+
|
5
|
+
Release on Aug XX, 2024
|
6
|
+
|
7
|
+
* dynamic storage classes for volumes
|
8
|
+
* dynamic templates available for compute
|
9
|
+
* dynamic templates available for databases
|
10
|
+
* fixed bug: context switched to cfg.json after CGC update
|
11
|
+
* updated command: `cgc status`, now it is fully dynamic
|
12
|
+
* add flag `-y` to `cgc compute port` group commands
|
13
|
+
* `active_deadline_seconds` to `cgc job create` command
|
14
|
+
* `cgc db create` now have option `-d` available
|
15
|
+
* updated displayed values with `cgc job list`
|
16
|
+
* updated version control messages
|
17
|
+
|
3
18
|
## 1.0.15
|
4
19
|
|
5
20
|
Release on June 27, 2024
|
@@ -3,8 +3,13 @@ import os
|
|
3
3
|
|
4
4
|
from cgc.commands.auth import auth_utils
|
5
5
|
from cgc.commands.auth import auth_logic
|
6
|
-
from cgc.utils.consts.env_consts import TMP_DIR
|
7
|
-
from cgc.utils.config_utils import
|
6
|
+
from cgc.utils.consts.env_consts import TMP_DIR
|
7
|
+
from cgc.utils.config_utils import (
|
8
|
+
get_config_path,
|
9
|
+
save_to_config,
|
10
|
+
get_config_file_name,
|
11
|
+
save_to_local_config_context,
|
12
|
+
)
|
8
13
|
from cgc.utils.message_utils import key_error_decorator_for_helpers
|
9
14
|
|
10
15
|
|
@@ -17,6 +22,7 @@ def auth_register_response(
|
|
17
22
|
aes_key, password = auth_utils.get_aes_key_and_password(unzip_dir, priv_key_bytes)
|
18
23
|
|
19
24
|
os.environ["CONFIG_FILE_NAME"] = config_filename
|
25
|
+
save_to_local_config_context(config_filename)
|
20
26
|
save_to_config(
|
21
27
|
user_id=user_id,
|
22
28
|
password=password,
|
@@ -27,8 +33,8 @@ def auth_register_response(
|
|
27
33
|
)
|
28
34
|
auth_logic.auth_create_api_key_with_save()
|
29
35
|
shutil.rmtree(TMP_DIR_PATH)
|
30
|
-
#
|
31
|
-
if config_filename == "
|
36
|
+
# cfg.json
|
37
|
+
if config_filename == "cfg.json":
|
32
38
|
return f"Register successful! You can now use the CLI. Saved data to:{os.path.join(get_config_path(),config_filename)}\n\
|
33
39
|
Consider backup this file. It stores data accessible only to you with which you can access CGC platform."
|
34
40
|
return f"New context created successfully! \nNew config file saved to: {os.path.join(get_config_path(),config_filename)}\n\
|
cgc/commands/cgc_cmd.py
CHANGED
@@ -12,10 +12,11 @@ from cgc.utils.prepare_headers import get_api_url_and_prepare_headers
|
|
12
12
|
from cgc.utils.response_utils import retrieve_and_validate_response_send_metric
|
13
13
|
from cgc.telemetry.basic import telemetry_permission_set
|
14
14
|
from cgc.commands.compute.compute_responses import compute_logs_response
|
15
|
+
|
15
16
|
# from cgc.commands.auth.auth_cmd import auth_register
|
16
17
|
from cgc.utils import set_environment_data, check_if_config_exist, list_all_config_files
|
17
18
|
from cgc.commands.cgc_helpers import table_of_user_context_files
|
18
|
-
from cgc.utils.config_utils import config_path
|
19
|
+
from cgc.utils.config_utils import config_path, save_to_local_config_context
|
19
20
|
from cgc.utils.consts.env_consts import ENV_FILE_PATH
|
20
21
|
|
21
22
|
|
@@ -100,6 +101,7 @@ def switch_context(number: int):
|
|
100
101
|
# config_filename=file_name,
|
101
102
|
# )
|
102
103
|
set_environment_data("CONFIG_FILE_NAME", file_name)
|
104
|
+
save_to_local_config_context(file_name)
|
103
105
|
click.echo(f"Context file changed to: {file_name}")
|
104
106
|
|
105
107
|
|
@@ -1,7 +1,59 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
from typing import Union
|
1
3
|
from tabulate import tabulate
|
2
4
|
from cgc.utils.message_utils import key_error_decorator_for_helpers
|
3
5
|
|
4
6
|
|
7
|
+
def _resource_match(resource: str) -> Union[str, None]:
|
8
|
+
if resource == "requests.cpu":
|
9
|
+
return "Total CPU"
|
10
|
+
elif resource == "requests.memory":
|
11
|
+
return "Total RAM"
|
12
|
+
elif resource == "requests.nvidia.com/gpu":
|
13
|
+
return "Total GPU"
|
14
|
+
elif resource.startswith("requests.comtegra.cloud/"):
|
15
|
+
gpu_type = resource.split("/")[-1]
|
16
|
+
return f"GPU {gpu_type.upper()}"
|
17
|
+
elif resource == "persistentvolumeclaims":
|
18
|
+
return "Volume Count"
|
19
|
+
elif resource == "requests.storage":
|
20
|
+
return "Total Storage"
|
21
|
+
elif resource.endswith(".storageclass.storage.k8s.io/requests.storage"):
|
22
|
+
storage_class = resource.split(".")[0]
|
23
|
+
return f"Storage ({storage_class})"
|
24
|
+
else:
|
25
|
+
return None
|
26
|
+
|
27
|
+
|
28
|
+
class ResourceOrder(Enum):
|
29
|
+
CPU = 1
|
30
|
+
MEMORY = 2
|
31
|
+
GPU = 3
|
32
|
+
STORAGE = 4
|
33
|
+
VOLUME = 5
|
34
|
+
GPU_TYPE = 6
|
35
|
+
OTHER = 7
|
36
|
+
|
37
|
+
|
38
|
+
def _resource_order(resource: str) -> ResourceOrder:
|
39
|
+
if resource == "requests.cpu":
|
40
|
+
return ResourceOrder.CPU
|
41
|
+
elif resource == "requests.memory":
|
42
|
+
return ResourceOrder.MEMORY
|
43
|
+
elif resource == "requests.nvidia.com/gpu":
|
44
|
+
return ResourceOrder.GPU
|
45
|
+
elif resource.startswith("requests.comtegra.cloud/"):
|
46
|
+
return ResourceOrder.GPU_TYPE
|
47
|
+
elif resource == "persistentvolumeclaims":
|
48
|
+
return ResourceOrder.VOLUME
|
49
|
+
elif resource == "requests.storage":
|
50
|
+
return ResourceOrder.STORAGE
|
51
|
+
elif resource.endswith(".storageclass.storage.k8s.io/requests.storage"):
|
52
|
+
return ResourceOrder.STORAGE
|
53
|
+
else:
|
54
|
+
return ResourceOrder.OTHER
|
55
|
+
|
56
|
+
|
5
57
|
@key_error_decorator_for_helpers
|
6
58
|
def cgc_status_response(data: dict):
|
7
59
|
"""Generates and prints resource limits and available resources in a pretty format
|
@@ -12,54 +64,31 @@ def cgc_status_response(data: dict):
|
|
12
64
|
:type metric_error: str
|
13
65
|
"""
|
14
66
|
|
15
|
-
resources_available = data["details"]["available_resources"]
|
16
|
-
resources_limits = data["details"]["limits_resources"]
|
17
|
-
list_headers = ["Resource", "
|
18
|
-
resource_names = [
|
19
|
-
"CPU",
|
20
|
-
"RAM",
|
21
|
-
"GPU",
|
22
|
-
"-",
|
23
|
-
"-",
|
24
|
-
# "-",
|
25
|
-
"Volume",
|
26
|
-
"Storage",
|
27
|
-
"-",
|
28
|
-
"-",
|
29
|
-
]
|
30
|
-
types = [
|
31
|
-
"",
|
32
|
-
"",
|
33
|
-
"",
|
34
|
-
"A100",
|
35
|
-
"A5000",
|
36
|
-
# "V100",
|
37
|
-
"Count",
|
38
|
-
"",
|
39
|
-
"nvme",
|
40
|
-
"ssd",
|
41
|
-
]
|
42
|
-
resource_keys = [
|
43
|
-
"requests.cpu",
|
44
|
-
"requests.memory",
|
45
|
-
"requests.nvidia.com/gpu",
|
46
|
-
"requests.comtegra.cloud/a100",
|
47
|
-
"requests.comtegra.cloud/a5000",
|
48
|
-
# "requests.comtegra.cloud/v100",
|
49
|
-
"persistentvolumeclaims",
|
50
|
-
"requests.storage",
|
51
|
-
"nvme-rwx.storageclass.storage.k8s.io/requests.storage",
|
52
|
-
"ssd-rwx.storageclass.storage.k8s.io/requests.storage",
|
53
|
-
]
|
67
|
+
resources_available: dict = data["details"]["available_resources"]
|
68
|
+
resources_limits: dict = data["details"]["limits_resources"]
|
69
|
+
list_headers = ["Resource", "Available", "Limit"]
|
70
|
+
resource_names = []
|
54
71
|
|
55
72
|
resources_available_list = []
|
56
73
|
resources_limits_list = []
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
74
|
+
resources_available = {
|
75
|
+
key: value
|
76
|
+
for key, value in sorted(
|
77
|
+
resources_available.items(),
|
78
|
+
key=lambda x: (_resource_order(x[0]).value, x[1]),
|
79
|
+
)
|
80
|
+
}
|
81
|
+
for resource, available in resources_available.items():
|
82
|
+
if resource not in resources_limits.keys():
|
83
|
+
continue
|
84
|
+
resource_parsed = _resource_match(resource)
|
85
|
+
if resource_parsed is None:
|
86
|
+
continue
|
87
|
+
resource_names.append(resource_parsed)
|
88
|
+
limit = resources_limits[resource]
|
89
|
+
if "storage" in resource or "memory" in resource:
|
90
|
+
available = f"{available} GB"
|
91
|
+
limit = f"{limit} GB"
|
63
92
|
else:
|
64
93
|
available = int(available)
|
65
94
|
limit = int(limit)
|
@@ -67,9 +96,7 @@ def cgc_status_response(data: dict):
|
|
67
96
|
resources_limits_list.append(limit)
|
68
97
|
|
69
98
|
return tabulate(
|
70
|
-
list(
|
71
|
-
zip(resource_names, types, resources_available_list, resources_limits_list)
|
72
|
-
),
|
99
|
+
list(zip(resource_names, resources_available_list, resources_limits_list)),
|
73
100
|
headers=list_headers,
|
74
101
|
)
|
75
102
|
|
cgc/commands/cgc_models.py
CHANGED
@@ -1,9 +1,17 @@
|
|
1
|
-
from
|
1
|
+
from abc import ABC, abstractmethod
|
2
2
|
|
3
3
|
|
4
|
-
class CGCEntityList(
|
4
|
+
class CGCEntityList(ABC):
|
5
5
|
"""Base class for other lists"""
|
6
6
|
|
7
|
+
@staticmethod
|
8
|
+
@abstractmethod
|
9
|
+
def load_data() -> list[str]:
|
10
|
+
pass
|
11
|
+
|
7
12
|
@classmethod
|
8
13
|
def get_list(cls) -> list[str]:
|
9
|
-
|
14
|
+
try:
|
15
|
+
return [el for el in cls.load_data()]
|
16
|
+
except TypeError:
|
17
|
+
return []
|
@@ -96,9 +96,21 @@ def compute_filebrowser_delete():
|
|
96
96
|
default=True,
|
97
97
|
help="If set, port will NOT be exposed to the internet. By default port is exposed to the internet.",
|
98
98
|
)
|
99
|
-
|
99
|
+
@click.option(
|
100
|
+
"-y",
|
101
|
+
"--yes",
|
102
|
+
"yes",
|
103
|
+
is_flag=True,
|
104
|
+
type=click.BOOL,
|
105
|
+
required=False,
|
106
|
+
default=False,
|
107
|
+
help="If set, command will not ask for confirmation",
|
108
|
+
)
|
109
|
+
def compute_port_add(
|
110
|
+
app_name: str, port_name: str, port: int, ingress: bool, yes: bool
|
111
|
+
):
|
100
112
|
"""Add a port to a running resource"""
|
101
|
-
while True:
|
113
|
+
while True and not yes:
|
102
114
|
click.echo(
|
103
115
|
'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.'
|
104
116
|
)
|
@@ -161,9 +173,21 @@ def compute_port_add(app_name: str, port_name: str, port: int, ingress: bool):
|
|
161
173
|
default=True,
|
162
174
|
help="If set, port will NOT be exposed to the internet. By default port is exposed to the internet.",
|
163
175
|
)
|
164
|
-
|
176
|
+
@click.option(
|
177
|
+
"-y",
|
178
|
+
"--yes",
|
179
|
+
"yes",
|
180
|
+
is_flag=True,
|
181
|
+
type=click.BOOL,
|
182
|
+
required=False,
|
183
|
+
default=False,
|
184
|
+
help="If set, command will not ask for confirmation",
|
185
|
+
)
|
186
|
+
def compute_port_update(
|
187
|
+
app_name: str, port_name: str, port: int, ingress: bool, yes: bool
|
188
|
+
):
|
165
189
|
"""Update a port in a running resource"""
|
166
|
-
while True:
|
190
|
+
while True and not yes:
|
167
191
|
click.echo(
|
168
192
|
'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.'
|
169
193
|
)
|
@@ -204,9 +228,19 @@ def compute_port_update(app_name: str, port_name: str, port: int, ingress: bool)
|
|
204
228
|
required=True,
|
205
229
|
help="Name of port",
|
206
230
|
)
|
207
|
-
|
231
|
+
@click.option(
|
232
|
+
"-y",
|
233
|
+
"--yes",
|
234
|
+
"yes",
|
235
|
+
is_flag=True,
|
236
|
+
type=click.BOOL,
|
237
|
+
required=False,
|
238
|
+
default=False,
|
239
|
+
help="If set, command will not ask for confirmation",
|
240
|
+
)
|
241
|
+
def compute_port_delete(app_name: str, port_name: str, yes: bool):
|
208
242
|
"""Delete a port from a running resource"""
|
209
|
-
while True:
|
243
|
+
while True and not yes:
|
210
244
|
click.echo(
|
211
245
|
'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.'
|
212
246
|
)
|
@@ -1,28 +1,23 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
|
1
3
|
from cgc.commands.cgc_models import CGCEntityList
|
4
|
+
from cgc.utils.config_utils import read_from_local_cfg
|
5
|
+
import cgc.utils.version_control as version_control
|
2
6
|
|
3
7
|
|
4
8
|
class ComputesList(CGCEntityList):
|
5
|
-
"""List of templates in cgc-server
|
9
|
+
"""List of compute templates in cgc-server"""
|
10
|
+
|
11
|
+
@staticmethod
|
12
|
+
def load_data() -> list[str]:
|
13
|
+
try:
|
14
|
+
return read_from_local_cfg("compute_templates")
|
15
|
+
except FileNotFoundError:
|
16
|
+
version_control.get_server_version() # possible loop on error
|
17
|
+
return ComputesList.load_data()
|
6
18
|
|
7
|
-
:param Enum: name of template
|
8
|
-
:type Enum: str
|
9
|
-
"""
|
10
19
|
|
11
|
-
|
12
|
-
NVIDIA_RAPIDS = "nvidia-rapids"
|
13
|
-
NVIDIA_PYTORCH = "nvidia-pytorch"
|
14
|
-
NVIDIA_TRITON = "nvidia-triton"
|
15
|
-
UNSLOTH_CU121 = "unsloth-cu121"
|
16
|
-
NGINX = "nginx"
|
17
|
-
LABEL_STUDIO = "label-studio"
|
18
|
-
COMFY_UI = "comfy-ui"
|
19
|
-
RAG = "rag"
|
20
|
-
DEEPSTREAM = "deepstream"
|
21
|
-
T2V_TRANSFORMERS = "t2v-transformers"
|
22
|
-
CUSTOM = "custom"
|
23
|
-
|
24
|
-
|
25
|
-
class GPUsList(CGCEntityList):
|
20
|
+
class GPUsList(Enum):
|
26
21
|
"""List of templates in cgc-server
|
27
22
|
|
28
23
|
:param Enum: name of template
|
@@ -35,3 +30,6 @@ class GPUsList(CGCEntityList):
|
|
35
30
|
H100 = "H100"
|
36
31
|
P40 = "P40"
|
37
32
|
P100 = "P100"
|
33
|
+
|
34
|
+
def get_list() -> list[str]:
|
35
|
+
return [el.value for el in GPUsList]
|
cgc/commands/db/db_cmd.py
CHANGED
@@ -46,11 +46,19 @@ def db_group():
|
|
46
46
|
multiple=True,
|
47
47
|
help="Volume name to be mounted with default mount path",
|
48
48
|
)
|
49
|
+
@click.option(
|
50
|
+
"-d",
|
51
|
+
"--resource-data",
|
52
|
+
"resource_data",
|
53
|
+
multiple=True,
|
54
|
+
help="List of optional arguments to be passed to the app, key=value format",
|
55
|
+
)
|
49
56
|
def db_create(
|
50
57
|
entity: str,
|
51
58
|
cpu: int,
|
52
59
|
memory: int,
|
53
60
|
volumes: list[str],
|
61
|
+
resource_data: list[str],
|
54
62
|
name: str,
|
55
63
|
):
|
56
64
|
"""
|
@@ -81,6 +89,7 @@ def db_create(
|
|
81
89
|
cpu=cpu,
|
82
90
|
memory=memory,
|
83
91
|
volumes=volumes,
|
92
|
+
resource_data=resource_data,
|
84
93
|
)
|
85
94
|
__res = call_api(
|
86
95
|
request=EndpointTypes.post,
|
cgc/commands/db/db_models.py
CHANGED
@@ -1,19 +1,21 @@
|
|
1
1
|
from cgc.commands.cgc_models import CGCEntityList
|
2
2
|
from cgc.commands.exceptions import DatabaseCreationException
|
3
|
+
from cgc.utils.config_utils import read_from_local_cfg
|
4
|
+
import cgc.utils.version_control as version_control
|
3
5
|
|
4
6
|
|
5
7
|
class DatabasesList(CGCEntityList):
|
6
|
-
"""List of templates in cgc-server
|
8
|
+
"""List of database templates in cgc-server"""
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
@staticmethod
|
11
|
+
def load_data() -> list[str]:
|
12
|
+
try:
|
13
|
+
list_of_templates = read_from_local_cfg("database_templates")
|
14
|
+
except FileNotFoundError:
|
15
|
+
version_control.get_server_version() # possible loop on error
|
16
|
+
return DatabasesList.load_data()
|
17
|
+
list_of_templates.extend(["mongodb", "redis"])
|
18
|
+
return list_of_templates
|
17
19
|
|
18
20
|
@staticmethod
|
19
21
|
def verify(entity: str) -> str:
|
cgc/commands/jobs/job_utils.py
CHANGED
@@ -28,6 +28,7 @@ def job_create_payload(
|
|
28
28
|
startup_command: str = "",
|
29
29
|
repository_secret: str = "",
|
30
30
|
ttl_seconds_after_finished: Optional[int] = None,
|
31
|
+
active_deadline_seconds: Optional[int] = None,
|
31
32
|
):
|
32
33
|
"""
|
33
34
|
Create payload for app creation.
|
@@ -38,6 +39,8 @@ def job_create_payload(
|
|
38
39
|
|
39
40
|
if ttl_seconds_after_finished is not None:
|
40
41
|
extra_payload["ttl_seconds_after_finished"] = ttl_seconds_after_finished
|
42
|
+
if active_deadline_seconds is not None:
|
43
|
+
extra_payload["active_deadline_seconds"] = active_deadline_seconds
|
41
44
|
|
42
45
|
payload = {
|
43
46
|
"resource_data": {
|
@@ -111,13 +114,17 @@ def get_job_list(job_list: list, job_pod_list: list):
|
|
111
114
|
|
112
115
|
for i, job_data in enumerate(job_list):
|
113
116
|
list_of_json_job_data[i]["name"] = job_data.get("name", "")
|
114
|
-
list_of_json_job_data[i]["
|
117
|
+
list_of_json_job_data[i]["ttl"] = job_data.get(
|
115
118
|
"ttl_seconds_after_finished", "N/A"
|
116
119
|
)
|
120
|
+
list_of_json_job_data[i]["ads"] = job_data.get("active_deadline_seconds", "N/A")
|
117
121
|
for job in list_of_json_job_data:
|
118
122
|
for job_pod in job_pod_list:
|
119
|
-
|
120
|
-
|
123
|
+
job_pod_labels: dict = job_pod.get("labels", {})
|
124
|
+
if job_pod_labels.get("app-name", "") == job.get("name"):
|
125
|
+
job["status"] = job_pod["status"]
|
126
|
+
job["gpu-count"] = job_pod_labels.get("gpu-count", 0)
|
127
|
+
job["gpu-label"] = job_pod_labels.get("gpu-label", "N/A")
|
121
128
|
# job["status_reason"] = [
|
122
129
|
# x.get("reason", "N/A") for x in job_pod.get("status_reasons", [])
|
123
130
|
# ]
|
@@ -159,12 +166,12 @@ def get_job_json_data(job_list: list):
|
|
159
166
|
|
160
167
|
job_data = {
|
161
168
|
"name": job.get("labels", {}).get("app-name"),
|
162
|
-
"status": job.get("status", {}).get(
|
169
|
+
"status": job.get("status", {}).get(
|
170
|
+
"phase", "Unknown"
|
171
|
+
), # only happen when Pod "disappeared" from the k8s - BUG
|
163
172
|
"volumes_mounted": volumes_mounted,
|
164
173
|
"cpu": cpu,
|
165
174
|
"ram": ram,
|
166
|
-
"gpu-count": job.get("labels", {}).get("gpu-count", 0),
|
167
|
-
"gpu-label": job.get("labels", {}).get("gpu-label", "N/A"),
|
168
175
|
}
|
169
176
|
# getting rid of unwanted and used values
|
170
177
|
if "pod-template-hash" in job["labels"].keys():
|
cgc/commands/jobs/jobs_cmd.py
CHANGED
@@ -155,6 +155,13 @@ def job_list():
|
|
155
155
|
default=None,
|
156
156
|
help="Time to live in seconds after app is finished",
|
157
157
|
)
|
158
|
+
@click.option(
|
159
|
+
"--ads",
|
160
|
+
"active_deadline_seconds",
|
161
|
+
type=click.INT,
|
162
|
+
default=None,
|
163
|
+
help="Time to live in seconds after app is started",
|
164
|
+
)
|
158
165
|
def job_create(
|
159
166
|
gpu: int,
|
160
167
|
gpu_type: str,
|
@@ -170,6 +177,7 @@ def job_create(
|
|
170
177
|
startup_command: str,
|
171
178
|
repository_secret: str,
|
172
179
|
ttl_seconds_after_finished: int,
|
180
|
+
active_deadline_seconds: int,
|
173
181
|
):
|
174
182
|
"""
|
175
183
|
Create job in user namespace.
|
@@ -200,6 +208,8 @@ def job_create(
|
|
200
208
|
:type repository_secret: str
|
201
209
|
:param ttl_seconds_after_finished: time to live in seconds after app is finished
|
202
210
|
:type ttl_seconds_after_finished: int
|
211
|
+
:param active_deadline_seconds: time to live in seconds after app is started
|
212
|
+
:type active_deadline_seconds: int
|
203
213
|
"""
|
204
214
|
api_url, headers = get_api_url_and_prepare_headers()
|
205
215
|
url = f"{api_url}/v1/api/job/create"
|
@@ -226,6 +236,7 @@ def job_create(
|
|
226
236
|
startup_command=cleaned_data,
|
227
237
|
repository_secret=repository_secret,
|
228
238
|
ttl_seconds_after_finished=ttl_seconds_after_finished,
|
239
|
+
active_deadline_seconds=active_deadline_seconds,
|
229
240
|
)
|
230
241
|
__res = call_api(
|
231
242
|
request=EndpointTypes.post,
|
@@ -1,12 +1,11 @@
|
|
1
|
-
def volume_create_payload_validator(name,
|
1
|
+
def volume_create_payload_validator(name, size, storage_class):
|
2
2
|
"""
|
3
3
|
Create payload for volume creation.
|
4
4
|
"""
|
5
5
|
payload = {
|
6
6
|
"name": name,
|
7
|
-
"
|
7
|
+
"storage_class": storage_class,
|
8
8
|
"size": size,
|
9
|
-
"disks_type": disk_type,
|
10
9
|
"auto_mount": "false",
|
11
10
|
}
|
12
11
|
return payload
|
@@ -8,7 +8,9 @@ from cgc.commands.volume.data_model import (
|
|
8
8
|
volume_mount_payload_validator,
|
9
9
|
volume_umount_payload_validator,
|
10
10
|
)
|
11
|
+
from cgc.commands.volume.volume_models import StorageClassList
|
11
12
|
from cgc.commands.volume.volume_responses import (
|
13
|
+
volume_storage_class_details_response,
|
12
14
|
volume_list_response,
|
13
15
|
volume_mount_response,
|
14
16
|
volume_umount_response,
|
@@ -47,6 +49,29 @@ def volume_list():
|
|
47
49
|
)
|
48
50
|
|
49
51
|
|
52
|
+
@volume_group.command("details", cls=CustomCommand)
|
53
|
+
@click.argument("storage_class")
|
54
|
+
def volume_storage_class_details(storage_class: str):
|
55
|
+
"""Get details of storage class.
|
56
|
+
\f
|
57
|
+
:param storage_class: type of disk
|
58
|
+
:type storage_class: str
|
59
|
+
"""
|
60
|
+
api_url, headers = get_api_url_and_prepare_headers()
|
61
|
+
url = f"{api_url}/v1/api/storage/volume/details/{storage_class}"
|
62
|
+
metric = "volume.details"
|
63
|
+
__res = call_api(
|
64
|
+
request=EndpointTypes.get,
|
65
|
+
url=url,
|
66
|
+
headers=headers,
|
67
|
+
)
|
68
|
+
click.echo(
|
69
|
+
volume_storage_class_details_response(
|
70
|
+
retrieve_and_validate_response_send_metric(__res, metric)
|
71
|
+
)
|
72
|
+
)
|
73
|
+
|
74
|
+
|
50
75
|
@volume_group.command("create", cls=CustomCommand)
|
51
76
|
@click.argument("name")
|
52
77
|
@click.option(
|
@@ -58,38 +83,28 @@ def volume_list():
|
|
58
83
|
required=True,
|
59
84
|
)
|
60
85
|
@click.option(
|
61
|
-
"-
|
62
|
-
"--
|
63
|
-
"
|
64
|
-
type=click.Choice([
|
65
|
-
default=
|
86
|
+
"-sc",
|
87
|
+
"--storage-class",
|
88
|
+
"storage_class",
|
89
|
+
type=click.Choice([*StorageClassList.load_data()]),
|
90
|
+
default=StorageClassList.load_default_storage_class(),
|
66
91
|
help="Type of disk",
|
67
92
|
)
|
68
|
-
|
69
|
-
"-a",
|
70
|
-
"--access",
|
71
|
-
"access",
|
72
|
-
type=click.Choice(["rwx", "rwo"]),
|
73
|
-
default="rwx",
|
74
|
-
help="Volume access mode",
|
75
|
-
)
|
76
|
-
def volume_create(name: str, size: int, disk_type: str, access: str):
|
93
|
+
def volume_create(name: str, size: int, storage_class: str):
|
77
94
|
"""Create volume in user namespace.
|
78
95
|
\f
|
79
96
|
:param name: name of volume
|
80
97
|
:type name: str
|
81
98
|
:param size: size of volume in GiB
|
82
99
|
:type size: int
|
83
|
-
:param
|
84
|
-
:type
|
85
|
-
:param access: access type of volume - RWO or RWX
|
86
|
-
:type access: str
|
100
|
+
:param storage_class: type of disk
|
101
|
+
:type storage_class: str
|
87
102
|
"""
|
88
103
|
api_url, headers = get_api_url_and_prepare_headers()
|
89
104
|
url = f"{api_url}/v1/api/storage/volume/create"
|
90
105
|
metric = "volume.create"
|
91
106
|
__payload = volume_create_payload_validator(
|
92
|
-
name=name,
|
107
|
+
name=name, size=size, storage_class=storage_class
|
93
108
|
)
|
94
109
|
__res = call_api(
|
95
110
|
request=EndpointTypes.post,
|
@@ -0,0 +1,22 @@
|
|
1
|
+
from cgc.utils.config_utils import read_from_local_cfg
|
2
|
+
import cgc.utils.version_control as version_control
|
3
|
+
|
4
|
+
|
5
|
+
class StorageClassList:
|
6
|
+
"""List of compute templates in cgc-server"""
|
7
|
+
|
8
|
+
@staticmethod
|
9
|
+
def load_data() -> list[str]:
|
10
|
+
try:
|
11
|
+
return read_from_local_cfg("storage_classes")
|
12
|
+
except FileNotFoundError:
|
13
|
+
version_control.get_server_version() # possible loop on error
|
14
|
+
return StorageClassList.load_data()
|
15
|
+
|
16
|
+
@staticmethod
|
17
|
+
def load_default_storage_class() -> str:
|
18
|
+
try:
|
19
|
+
return read_from_local_cfg("default_storage_class")
|
20
|
+
except FileNotFoundError:
|
21
|
+
version_control.get_server_version() # possible loop on error
|
22
|
+
return StorageClassList.load_default_storage_class()
|
@@ -33,6 +33,29 @@ def volume_list_response(data: dict) -> str:
|
|
33
33
|
return tabulate(volume_list_to_print, headers=list_headers)
|
34
34
|
|
35
35
|
|
36
|
+
@key_error_decorator_for_helpers
|
37
|
+
def volume_storage_class_details_response(data: dict) -> str:
|
38
|
+
"""Create response string for volume storage class details command.
|
39
|
+
|
40
|
+
:return: Response string.
|
41
|
+
:rtype: str
|
42
|
+
"""
|
43
|
+
# ["details"]["storage_class"] -> storage class name
|
44
|
+
# ["details"]["storage_class_info"] -> billing_cost, storage_type, reclaim_policy, volume_binding_mode
|
45
|
+
|
46
|
+
storage_class_headers = [
|
47
|
+
"name",
|
48
|
+
*data["details"]["storage_class_info"].keys(),
|
49
|
+
]
|
50
|
+
storage_class_data = [
|
51
|
+
(
|
52
|
+
data["details"]["storage_class"],
|
53
|
+
*data["details"]["storage_class_info"].values(),
|
54
|
+
)
|
55
|
+
]
|
56
|
+
return tabulate(storage_class_data, headers=storage_class_headers)
|
57
|
+
|
58
|
+
|
36
59
|
@key_error_decorator_for_helpers
|
37
60
|
def volume_create_response(data: dict) -> str:
|
38
61
|
"""Create response string for volume create command.
|
@@ -48,7 +71,10 @@ def volume_create_response(data: dict) -> str:
|
|
48
71
|
/ 1000
|
49
72
|
)
|
50
73
|
access = data["details"]["volume_created"]["access_type"][0]
|
51
|
-
|
74
|
+
try:
|
75
|
+
disk_type = data["details"]["volume_created"]["disks_type"]
|
76
|
+
except KeyError:
|
77
|
+
disk_type = data["details"]["storage_class"]
|
52
78
|
change_gauge(f"{get_namespace()}.volume.count", 1)
|
53
79
|
change_gauge(f"{get_namespace()}.volume.totalSizeAccumulated", size)
|
54
80
|
|
cgc/telemetry/basic.py
CHANGED
@@ -75,7 +75,7 @@ def increment_metric(metric, is_error: bool = False):
|
|
75
75
|
if is_error:
|
76
76
|
metric = f"{metric}.error"
|
77
77
|
click.echo(
|
78
|
-
|
78
|
+
"If you want to open support request, attach command used, status code and error message via support system at https://support.comtegra.pl/"
|
79
79
|
)
|
80
80
|
else:
|
81
81
|
metric = f"{metric}.ok"
|
cgc/utils/config_utils.py
CHANGED
@@ -1,11 +1,19 @@
|
|
1
1
|
import json
|
2
2
|
import os
|
3
3
|
import sys
|
4
|
+
from typing import Union
|
4
5
|
import click
|
5
6
|
|
6
7
|
from cgc.commands.auth import NoNamespaceInConfig, NoConfigFileFound
|
7
8
|
from cgc.utils.message_utils import prepare_error_message
|
8
|
-
from cgc.utils.consts.env_consts import CGC_API_URL, CGC_SECRET
|
9
|
+
from cgc.utils.consts.env_consts import CGC_API_URL, CGC_SECRET
|
10
|
+
|
11
|
+
|
12
|
+
def get_config_file_name():
|
13
|
+
try:
|
14
|
+
return read_from_local_cfg("context_filename")
|
15
|
+
except Exception:
|
16
|
+
return os.getenv("CONFIG_FILE_NAME")
|
9
17
|
|
10
18
|
|
11
19
|
def get_config_path():
|
@@ -32,6 +40,64 @@ def get_config_path():
|
|
32
40
|
config_path = get_config_path()
|
33
41
|
|
34
42
|
|
43
|
+
def save_to_local_config_context(context_filename: str):
|
44
|
+
"""Function allowing adding a variable number of key-value pairs to the config file.
|
45
|
+
If config file does not exist, it is created, otherwise key-value pairs are appended to existing config.
|
46
|
+
Values for existing keys are overwritten.
|
47
|
+
|
48
|
+
:param kwargs: key-value pairs to be saved in the config file
|
49
|
+
:type kwargs: dict
|
50
|
+
"""
|
51
|
+
read_cfg = {}
|
52
|
+
user_config_file = os.path.join(config_path, "cgc.json")
|
53
|
+
if not os.path.isdir(config_path):
|
54
|
+
os.makedirs(config_path)
|
55
|
+
try:
|
56
|
+
with open(user_config_file, "r", encoding="UTF-8") as f:
|
57
|
+
read_cfg = json.load(f)
|
58
|
+
except FileNotFoundError:
|
59
|
+
pass
|
60
|
+
except json.decoder.JSONDecodeError:
|
61
|
+
pass
|
62
|
+
|
63
|
+
with open(user_config_file, "w", encoding="UTF-8") as f:
|
64
|
+
final_cfg = {**read_cfg, "context_filename": context_filename}
|
65
|
+
json.dump(final_cfg, f)
|
66
|
+
|
67
|
+
|
68
|
+
def save_to_local_config(**kwargs):
|
69
|
+
"""Function allowing adding a variable number of key-value pairs to the config file.
|
70
|
+
If config file does not exist, it is created, otherwise key-value pairs are appended to existing config.
|
71
|
+
Values for existing keys are overwritten.
|
72
|
+
|
73
|
+
:param kwargs: key-value pairs to be saved in the config file
|
74
|
+
:type kwargs: dict
|
75
|
+
"""
|
76
|
+
read_cfg = {}
|
77
|
+
user_config_file = os.path.join(config_path, "cgc.json")
|
78
|
+
if not os.path.isdir(config_path):
|
79
|
+
os.makedirs(config_path)
|
80
|
+
try:
|
81
|
+
with open(user_config_file, "r", encoding="UTF-8") as f:
|
82
|
+
read_cfg = json.load(f)
|
83
|
+
except FileNotFoundError:
|
84
|
+
pass
|
85
|
+
except json.decoder.JSONDecodeError:
|
86
|
+
pass
|
87
|
+
|
88
|
+
with open(user_config_file, "w", encoding="UTF-8") as f:
|
89
|
+
cgc_api_url = read_from_cfg("cgc_api_url")
|
90
|
+
if "context_filename" in kwargs:
|
91
|
+
kwargs.pop("context_filename")
|
92
|
+
previous_server_values: dict = read_cfg.get(cgc_api_url, {})
|
93
|
+
previous_server_values.update(**kwargs)
|
94
|
+
final_cfg = {
|
95
|
+
**read_cfg,
|
96
|
+
cgc_api_url: {**previous_server_values},
|
97
|
+
}
|
98
|
+
json.dump(final_cfg, f)
|
99
|
+
|
100
|
+
|
35
101
|
def save_to_config(**kwargs):
|
36
102
|
"""Function allowing adding a variable number of key-value pairs to the config file.
|
37
103
|
If config file does not exist, it is created, otherwise key-value pairs are appended to existing config.
|
@@ -45,8 +111,8 @@ def save_to_config(**kwargs):
|
|
45
111
|
if not os.path.isdir(config_path):
|
46
112
|
os.makedirs(config_path)
|
47
113
|
try:
|
48
|
-
|
49
|
-
|
114
|
+
with open(user_config_file, "r", encoding="UTF-8") as f:
|
115
|
+
read_cfg = json.load(f)
|
50
116
|
except FileNotFoundError:
|
51
117
|
pass
|
52
118
|
|
@@ -70,6 +136,42 @@ def is_config_file_present():
|
|
70
136
|
return False
|
71
137
|
|
72
138
|
|
139
|
+
def read_from_local_cfg(key: str) -> Union[list, str]:
|
140
|
+
"""Function to read a single value from the CGC config
|
141
|
+
|
142
|
+
:param key: key name to read the value from config
|
143
|
+
:type key: str
|
144
|
+
:return: value for the provided key
|
145
|
+
:rtype: _type_
|
146
|
+
"""
|
147
|
+
filename_with_path = os.path.join(config_path, "cgc.json")
|
148
|
+
try:
|
149
|
+
with open(filename_with_path, "r", encoding="UTF-8") as f:
|
150
|
+
read_cfg: dict = json.load(f)
|
151
|
+
if key == "context_filename":
|
152
|
+
return read_cfg[key]
|
153
|
+
cgc_api_url = read_from_cfg("cgc_api_url")
|
154
|
+
if key is None:
|
155
|
+
return read_cfg[cgc_api_url]
|
156
|
+
return read_cfg[cgc_api_url][key]
|
157
|
+
except FileNotFoundError as e:
|
158
|
+
raise e
|
159
|
+
except KeyError as e:
|
160
|
+
if (
|
161
|
+
key == "compute_templates"
|
162
|
+
or key == "database_templates"
|
163
|
+
or key == "storage_classes"
|
164
|
+
):
|
165
|
+
return []
|
166
|
+
elif key == "default_storage_class":
|
167
|
+
return None
|
168
|
+
raise NoConfigFileFound() from e
|
169
|
+
except json.decoder.JSONDecodeError as e:
|
170
|
+
if key == "context_filename":
|
171
|
+
raise FileNotFoundError from e
|
172
|
+
return []
|
173
|
+
|
174
|
+
|
73
175
|
def read_from_cfg(key: str, filename=None):
|
74
176
|
"""Function to read a single value from config
|
75
177
|
|
@@ -83,7 +185,7 @@ def read_from_cfg(key: str, filename=None):
|
|
83
185
|
else:
|
84
186
|
filename_with_path = os.path.join(config_path, filename)
|
85
187
|
try:
|
86
|
-
with open(filename_with_path, "r
|
188
|
+
with open(filename_with_path, "r", encoding="UTF-8") as f:
|
87
189
|
read_cfg = json.load(f)
|
88
190
|
if key is None:
|
89
191
|
return read_cfg
|
cgc/utils/consts/env_consts.py
CHANGED
@@ -5,7 +5,7 @@ UNKNOWN_ERROR = "An unknown error occurred. Please try again or contact support
|
|
5
5
|
TIMEOUT_ERROR = (
|
6
6
|
"Connection timed out. Try again or contact support at support@comtegra.pl"
|
7
7
|
)
|
8
|
-
CONNECTION_ERROR = "
|
8
|
+
CONNECTION_ERROR = "Your current context server is not reachable. Try again or contact support at support@comtegra.pl"
|
9
9
|
CERTIFICATE_ERROR = " Could not find a suitable TLS CA certificate bundle, invalid path to server certificate."
|
10
10
|
|
11
11
|
DISABLED_ERROROUTDATED_MAJOR = (
|
cgc/utils/version_control.py
CHANGED
@@ -16,6 +16,8 @@ from cgc.utils.consts.message_consts import (
|
|
16
16
|
)
|
17
17
|
|
18
18
|
from cgc.utils.requests_helper import call_api, EndpointTypes
|
19
|
+
from cgc.utils.config_utils import save_to_local_config
|
20
|
+
import logging
|
19
21
|
|
20
22
|
|
21
23
|
def get_server_version():
|
@@ -29,11 +31,9 @@ def get_server_version():
|
|
29
31
|
try:
|
30
32
|
__res.raise_for_status()
|
31
33
|
except Exception:
|
32
|
-
|
33
|
-
"Your current context server is not available, cannot check server version.",
|
34
|
-
color="red",
|
35
|
-
)
|
34
|
+
logging.debug("Your current context server is not reachable.")
|
36
35
|
else:
|
36
|
+
save_to_local_config(**__res.json())
|
37
37
|
return __res.json()
|
38
38
|
|
39
39
|
|
@@ -59,8 +59,10 @@ def check_version():
|
|
59
59
|
"server_status"
|
60
60
|
] # braking change - 0.9.0, will not work with lower server version
|
61
61
|
server_version = f"{server_release}.{server_major}.{server_minor}"
|
62
|
-
client_version =
|
63
|
-
if
|
62
|
+
client_version = _get_version()
|
63
|
+
if (
|
64
|
+
server_major > MAJOR_VERSION and server_release == RELEASE
|
65
|
+
) or server_release > RELEASE:
|
64
66
|
click.echo(prepare_error_message(OUTDATED_MAJOR))
|
65
67
|
print_compare_versions(server_version, client_version)
|
66
68
|
while True:
|
@@ -93,6 +95,21 @@ def check_version():
|
|
93
95
|
f"Server at maintenance, current status: {server_status}"
|
94
96
|
)
|
95
97
|
)
|
98
|
+
if server_release == RELEASE and (
|
99
|
+
server_major < MAJOR_VERSION or server_minor < MINOR_VERSION
|
100
|
+
):
|
101
|
+
click.echo(
|
102
|
+
prepare_warning_message(
|
103
|
+
"You are using a newer client version than the server."
|
104
|
+
)
|
105
|
+
)
|
106
|
+
print_compare_versions(server_version, client_version)
|
107
|
+
elif server_release != RELEASE:
|
108
|
+
click.echo(
|
109
|
+
prepare_warning_message(
|
110
|
+
f"Server version is not compatible with the client version. Server version: {server_version}, Client version: {client_version}"
|
111
|
+
)
|
112
|
+
)
|
96
113
|
|
97
114
|
|
98
115
|
def _get_version():
|
@@ -1,22 +1,22 @@
|
|
1
|
-
cgc/.env,sha256=
|
2
|
-
cgc/CHANGELOG.md,sha256=
|
1
|
+
cgc/.env,sha256=VMwTqev7-Fa9XaP3Ofy8Th-Dl2GurIWONmQgSLCUl2A,210
|
2
|
+
cgc/CHANGELOG.md,sha256=gbcedsO6U2gjiefnjcOYV4czqaG0-QmtsSnykXZ_iL4,9625
|
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
|
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=xuRoMtO7VG0sZbOFrEGxPWRg94SBrgyRJDQif4el7UM,5031
|
8
|
+
cgc/commands/cgc_cmd_responses.py,sha256=0bkRIxfd7EiVdShOubxhDiUSlGj6yEZQL_NOHtu334k,3525
|
9
9
|
cgc/commands/cgc_helpers.py,sha256=ngArFjVw-8P_2g7J8k3b9xgDxfJw7JeaOtkhTySMSGU,1174
|
10
|
-
cgc/commands/cgc_models.py,sha256=
|
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
13
|
cgc/commands/auth/auth_cmd.py,sha256=f8_2oIlFQ1k0zvJXGfrwkdVoK5EJBwWesNdOcjtY_uM,4220
|
14
14
|
cgc/commands/auth/auth_logic.py,sha256=PzEyC6OBRSne_5dQT-cq69J0i8BqfGL0MIoXW-yM8qE,2281
|
15
|
-
cgc/commands/auth/auth_responses.py,sha256=
|
15
|
+
cgc/commands/auth/auth_responses.py,sha256=kIGyCbNqSQKhDsVpIIcbzR-1bFQHkC3ClbUPBNfKPwk,2095
|
16
16
|
cgc/commands/auth/auth_utils.py,sha256=8NZQQuR0JDCxRkNOHy8nRLDpeT3W87iNyaBI1owj79c,3597
|
17
17
|
cgc/commands/compute/__init__.py,sha256=lCdLfZ0ECSHtXEUSwq5YRHH85yXHchSsz8ZJvmClPtI,239
|
18
|
-
cgc/commands/compute/compute_cmd.py,sha256=
|
19
|
-
cgc/commands/compute/compute_models.py,sha256=
|
18
|
+
cgc/commands/compute/compute_cmd.py,sha256=YZYR0milioX8aShLUC5NSKWcJSimQx6d4WmweAgdCrw,16131
|
19
|
+
cgc/commands/compute/compute_models.py,sha256=MNwxG94fFJfaQ3kGlVXSJh1H5qPHLSU_6BnoiuWS7UA,860
|
20
20
|
cgc/commands/compute/compute_responses.py,sha256=DnRIZC_F1ZE887fMOXXYGYuYmB9n5x63sf8Ga_TL4Ww,5806
|
21
21
|
cgc/commands/compute/compute_utils.py,sha256=Pj4xa-C8-MzXF-CCnnHNXn0sXjHy_t-N7c-eig-vF1Y,9182
|
22
22
|
cgc/commands/compute/billing/__init__.py,sha256=ccjz-AzBCROjuR11qZRM4_62slI9ErmLi27xPUoRPHM,752
|
@@ -24,13 +24,13 @@ cgc/commands/compute/billing/billing_cmd.py,sha256=8lGf2GPyr09ZmqkRshmgfSPQgXa9o
|
|
24
24
|
cgc/commands/compute/billing/billing_responses.py,sha256=5vQSR_d41uizengzfXlHXL7XivO_73PpWdKmoUgqYNw,1965
|
25
25
|
cgc/commands/compute/billing/billing_utils.py,sha256=zXLbBBcWeOgur-r0OKiIjaKeaxMNxASXWzCTeTsyC6o,4711
|
26
26
|
cgc/commands/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
27
|
-
cgc/commands/db/db_cmd.py,sha256=
|
28
|
-
cgc/commands/db/db_models.py,sha256=
|
27
|
+
cgc/commands/db/db_cmd.py,sha256=cUS5t91QL_VfLaXJITYUjBiszoE-P8PYnm3u8VkmHEQ,3750
|
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=
|
33
|
-
cgc/commands/jobs/jobs_cmd.py,sha256=
|
32
|
+
cgc/commands/jobs/job_utils.py,sha256=LlgMK11XQ-yjl_eOTFYwdJwOxyeiSCNkZQRZFpFW14A,6560
|
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
|
36
36
|
cgc/commands/resource/resource_cmd.py,sha256=y2R6ZF0jFhTppD9JUpwLIcEdFRXg82Xw_5yrNIfG4mI,4089
|
@@ -45,9 +45,10 @@ cgc/commands/user/secret_models.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
|
|
45
45
|
cgc/commands/user/secret_responses.py,sha256=sg7HP5a0LnUvlo9w8oOcYASwI2cPeRnecjTVMa5yk7Y,1600
|
46
46
|
cgc/commands/user/secret_utils.py,sha256=70dn937rzecQ0eE-8zb9fLnBDWtjWjdQY1LJvHv3NzM,1623
|
47
47
|
cgc/commands/volume/__init__.py,sha256=Ou3kyb72aaXkrVCfQCVdniA65R2xHsRFgebooG1gflA,384
|
48
|
-
cgc/commands/volume/data_model.py,sha256=
|
49
|
-
cgc/commands/volume/volume_cmd.py,sha256=
|
50
|
-
cgc/commands/volume/
|
48
|
+
cgc/commands/volume/data_model.py,sha256=lLHuKRMe-5mgHL3i48QJn8_S3tJHFMCwu8cJAxXe-PU,1267
|
49
|
+
cgc/commands/volume/volume_cmd.py,sha256=Eylo_V8Tex8OF5IQcAlpo6ggC4FXYfnQunGtat6WwSs,8279
|
50
|
+
cgc/commands/volume/volume_models.py,sha256=eKHYLcAUezoJ1X2ENE-GE2CgE8lynlfT3Hs2PI8zAnY,779
|
51
|
+
cgc/commands/volume/volume_responses.py,sha256=lO6NiUFJkNTTKLerFihe3CeQwdo56WyUN5bk01dtfMs,4027
|
51
52
|
cgc/commands/volume/volume_utils.py,sha256=6IuDCNT-KAvUUF_EDg5cL9JewTGsbBsZlYd_zKHowCU,1973
|
52
53
|
cgc/sdk/__init__.py,sha256=m8uAD2e_ADbHC4_kaOpLrUk_bHy7wC56rPjhcttclCs,177
|
53
54
|
cgc/sdk/exceptions.py,sha256=99XIzDO6LYKjex715troH-MkGUN7hi2Bit4KHfSHDis,214
|
@@ -55,7 +56,7 @@ cgc/sdk/job.py,sha256=q9Vsarc3rKzurM4AOmbQDsQUVdyRqx0UzJVe_uO8xCU,5318
|
|
55
56
|
cgc/sdk/postgresql.py,sha256=ziXaMMwjSF3k1OAID3F9npqWVxreQaoZ8wn7X8x1FZw,1637
|
56
57
|
cgc/sdk/resource.py,sha256=w8SVRqrx0Mj1FS91Bt0oaMAfC4CDEcomgqzFCNgGaPc,13537
|
57
58
|
cgc/telemetry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
58
|
-
cgc/telemetry/basic.py,sha256=
|
59
|
+
cgc/telemetry/basic.py,sha256=h0f2yAanaflkEnetLjQvs_IR43KL0JmhJ30yrl6tOas,3174
|
59
60
|
cgc/tests/__init__.py,sha256=8aI3MVpkzaj0_UX02kZCtY5vmGO0rnq0mw2H04-OHf8,102743
|
60
61
|
cgc/tests/responses_tests.py,sha256=9vLOaUICeUXoDObeFwuu0FBTmC-hnJNZfzvolT96dGM,9188
|
61
62
|
cgc/tests/desired_responses/test_billing_invoice.txt,sha256=KR5m2gamn_bgfBdBmWDH2sPRJIPOw1u8kdH-gYE8jow,1579
|
@@ -68,7 +69,7 @@ cgc/tests/desired_responses/test_tabulate_response.txt,sha256=beNyCTS9fwrHn4ueEO
|
|
68
69
|
cgc/tests/desired_responses/test_volume_list.txt,sha256=vYB1p50BBHD801q7LUdDc_aca4ezQ8CFLWw7I-b4Uao,309
|
69
70
|
cgc/utils/__init__.py,sha256=JOvbqyGdFtswXE1TntOqM7XuIg5-t042WzAzvmX7Xtk,3661
|
70
71
|
cgc/utils/click_group.py,sha256=Scfw8eMIyt2dE1ezUq2JuiI-E_LklqXQXJEr7L-EG6A,633
|
71
|
-
cgc/utils/config_utils.py,sha256
|
72
|
+
cgc/utils/config_utils.py,sha256=W3aS1WhUmWEdHNUU3Zl3u7gTRceQWtfSf1dum2CqpVc,6947
|
72
73
|
cgc/utils/custom_exceptions.py,sha256=qvHdzaunZswZgN96iOHZIfLjehlJ79mcjqoMoW-tqEM,2628
|
73
74
|
cgc/utils/get_headers_data.py,sha256=JdEg5vrAHcWfsSJ7poYk3sNIY10OxX7YGVcmua-37lY,413
|
74
75
|
cgc/utils/message_utils.py,sha256=FAiUC-0zJiMhfPQAQC0ki1ZUs1vI_QqHwLmfoCDbLeU,1790
|
@@ -76,17 +77,17 @@ cgc/utils/prepare_headers.py,sha256=Hi3WNqtqydW56tNTLZmpfMTpu4aKCoDrLx4OcCGH9_U,
|
|
76
77
|
cgc/utils/requests_helper.py,sha256=Z89dTOTbSSi1xmtNPmAdJpduR9-DC12WEQsHYeuM9a0,2046
|
77
78
|
cgc/utils/response_utils.py,sha256=9vJqAt2UFJ1n-oesFPe6CB_ooGoStjl-twY_31Jt4_I,7374
|
78
79
|
cgc/utils/update.py,sha256=AsQwhcBqsjgNPKn6AN6ojt0Ew5otvJXyshys6bjr7DQ,413
|
79
|
-
cgc/utils/version_control.py,sha256=
|
80
|
+
cgc/utils/version_control.py,sha256=MTXpwFC0zl0xKZsllFHZMOUm0VuihA0u3g4jVW70c7w,4163
|
80
81
|
cgc/utils/consts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
81
|
-
cgc/utils/consts/env_consts.py,sha256=
|
82
|
-
cgc/utils/consts/message_consts.py,sha256=
|
82
|
+
cgc/utils/consts/env_consts.py,sha256=_yHjhXDBwYfLfUWfHOPlhGdzC0j9VFlkjKuSrvblBsU,1154
|
83
|
+
cgc/utils/consts/message_consts.py,sha256=xSW7XaqPIAO_uJr-eK2fcjP2o7t0OJ0OP75otbUBnAg,1232
|
83
84
|
cgc/utils/cryptography/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
84
85
|
cgc/utils/cryptography/aes_crypto.py,sha256=S0rKg38oy7rM5lTrP6DDpjLA-XRxuZggAXyxMFHtyzY,3333
|
85
86
|
cgc/utils/cryptography/encryption_module.py,sha256=rbblBBorHYPGl-iKblyZX3_NuPEvUTpnH1l_RgNGCbA,1958
|
86
87
|
cgc/utils/cryptography/rsa_crypto.py,sha256=h3jU5qPpj9uVjP1rTqZJTdYB5yjhD9HZpr_nD439h9Q,4180
|
87
|
-
cgcsdk-1.0.
|
88
|
-
cgcsdk-1.0.
|
89
|
-
cgcsdk-1.0.
|
90
|
-
cgcsdk-1.0.
|
91
|
-
cgcsdk-1.0.
|
92
|
-
cgcsdk-1.0.
|
88
|
+
cgcsdk-1.0.16.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
89
|
+
cgcsdk-1.0.16.dist-info/METADATA,sha256=omC-JocyzpOolm58Rv7yP2lYEqvBDwAfl8EPP_OsGSw,3091
|
90
|
+
cgcsdk-1.0.16.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
|
91
|
+
cgcsdk-1.0.16.dist-info/entry_points.txt,sha256=bdfIHeJ6Y-BBr5yupCVoK7SUrJj1yNdew8OtIOg_3No,36
|
92
|
+
cgcsdk-1.0.16.dist-info/top_level.txt,sha256=nqW9tqcIcCXFigQT69AuOk7XHKc4pCuv4HGJQGXb6iA,12
|
93
|
+
cgcsdk-1.0.16.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|