CGCSDK 0.1.10__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.
Files changed (44) hide show
  1. cgcsdk-0.1.10.dist-info/LICENSE +0 -0
  2. cgcsdk-0.1.10.dist-info/METADATA +36 -0
  3. cgcsdk-0.1.10.dist-info/RECORD +44 -0
  4. cgcsdk-0.1.10.dist-info/WHEEL +5 -0
  5. cgcsdk-0.1.10.dist-info/entry_points.txt +2 -0
  6. cgcsdk-0.1.10.dist-info/top_level.txt +2 -0
  7. src/.env +9 -0
  8. src/__init__.py +9 -0
  9. src/cgc.py +30 -0
  10. src/commands/__init__.py +0 -0
  11. src/commands/auth/__init__.py +0 -0
  12. src/commands/auth/auth_cmd.py +70 -0
  13. src/commands/auth/auth_utils.py +151 -0
  14. src/commands/billing/__init__.py +0 -0
  15. src/commands/billing/billing_cmd.py +115 -0
  16. src/commands/billing/billing_utils.py +36 -0
  17. src/commands/cgc_cmd.py +18 -0
  18. src/commands/compute/__init__.py +0 -0
  19. src/commands/compute/compute_cmd.py +220 -0
  20. src/commands/compute/compute_responses.py +156 -0
  21. src/commands/compute/compute_utills.py +96 -0
  22. src/commands/compute/data_model.py +27 -0
  23. src/commands/volume/__init__.py +0 -0
  24. src/commands/volume/data_model.py +46 -0
  25. src/commands/volume/volume_cmd.py +237 -0
  26. src/commands/volume/volume_responses.py +257 -0
  27. src/commands/volume/volume_utils.py +22 -0
  28. src/config.py +0 -0
  29. src/telemetry/__init__.py +0 -0
  30. src/telemetry/basic.py +44 -0
  31. src/utils/__init__.py +0 -0
  32. src/utils/click_group.py +17 -0
  33. src/utils/config_utils.py +89 -0
  34. src/utils/consts/__init__.py +0 -0
  35. src/utils/consts/message_consts.py +12 -0
  36. src/utils/consts/telemetry_consts.py +0 -0
  37. src/utils/cryptography/__init__.py +0 -0
  38. src/utils/cryptography/aes_crypto.py +100 -0
  39. src/utils/cryptography/encryption_module.py +55 -0
  40. src/utils/cryptography/rsa_crypto.py +126 -0
  41. src/utils/message_utils.py +28 -0
  42. src/utils/prepare_headers.py +86 -0
  43. src/utils/response_utils.py +25 -0
  44. src/utils/version_control.py +67 -0
File without changes
@@ -0,0 +1,36 @@
1
+ Metadata-Version: 2.1
2
+ Name: cgcsdk
3
+ Version: 0.1.10
4
+ Summary: Comtegra GPU Cloud REST API client
5
+ Home-page: https://github.com/Comtegra/cgc
6
+ Author: Comtegra AI Team
7
+ Author-email: info@comtegra.pl
8
+ License: BSD 2-clause
9
+ Keywords: cloud,sdk
10
+ Classifier: Development Status :: 1 - Planning
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: License :: OSI Approved :: BSD License
13
+ Classifier: Operating System :: POSIX :: Linux
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.4
16
+ Classifier: Programming Language :: Python :: 3.5
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: click
23
+ Requires-Dist: python-dotenv
24
+ Requires-Dist: tabulate
25
+ Requires-Dist: pycryptodomex
26
+ Requires-Dist: paramiko (>=2.11)
27
+ Requires-Dist: statsd
28
+ Requires-Dist: requests
29
+ Requires-Dist: setuptools
30
+
31
+ # CGC Client - K8s Cloud
32
+
33
+ cgc command line client for k8s Comtegra cloud environment.
34
+
35
+
36
+ ## Coming soon...
@@ -0,0 +1,44 @@
1
+ src/.env,sha256=2QcyU5ZdShTvd0arn0aysqpZJ-pRYrXo6Pd5IUIUqI0,192
2
+ src/__init__.py,sha256=XTwjaEQ5cyKyl7CnQlaEpK65FH_FD0DUj8GNLvkfpB4,279
3
+ src/cgc.py,sha256=8HB8pkb-DH894vlrxMwAfAkbQj7NP2utxpbOxyDOLZc,836
4
+ src/config.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ src/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ src/commands/cgc_cmd.py,sha256=gY7EmChPbPwHlOIMQzDXwLYgdSZslLkNhQuwRaNrRCc,470
7
+ src/commands/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ src/commands/auth/auth_cmd.py,sha256=g1slgpbcjJF6ajNWGCkTUyJ7hzZyMA4ruvl7Et5xRNw,2759
9
+ src/commands/auth/auth_utils.py,sha256=iAy1L8XEv4i-CukB1LODqHQwT2ZihNiUM_TKO1Kc4Qg,5175
10
+ src/commands/billing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ src/commands/billing/billing_cmd.py,sha256=_40vL9JCJMBMjyZyQlISlqBugm35EjsrH3FDRJN5J04,3303
12
+ src/commands/billing/billing_utils.py,sha256=hB9haCbBsnyvftS5t_XfGw8Mnp2mH0ZEo4P7bfsMPmY,1313
13
+ src/commands/compute/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ src/commands/compute/compute_cmd.py,sha256=5_S5jwCzKFyM0vzutcXTxtpkn5I-iiD1q_7yOouTC_Y,7101
15
+ src/commands/compute/compute_responses.py,sha256=P-r40lNmab0EO9rZ3OBcuhY9snN7tcrvsUhbH76zKFQ,6337
16
+ src/commands/compute/compute_utills.py,sha256=Cw_PPqw-MbStDCuxjZVWrWH3XPvA_SNfV9R48it2V1k,2844
17
+ src/commands/compute/data_model.py,sha256=y2XHsLau2wD5J8LJPoIlOOv8a5eYq5AIEK7MWheNcOI,584
18
+ src/commands/volume/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ src/commands/volume/data_model.py,sha256=c_aYwxoYOngW0yx-rQcUkF9LgugUp_CajTeOruI2_2o,976
20
+ src/commands/volume/volume_cmd.py,sha256=SfHOvw99G2VFtr6CT4RS0HfnPE41CnzhwGdJobOT8Qk,7990
21
+ src/commands/volume/volume_responses.py,sha256=8j44HgUiNikh2ZkeMwYIwq_JlgVJ63s7i-q_JESdkSI,10476
22
+ src/commands/volume/volume_utils.py,sha256=hxY6N7vmZ7Aitrb-gQ_rEKt_isOE3N_tX6tfnb3ZsIg,969
23
+ src/telemetry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ src/telemetry/basic.py,sha256=_358FABtHCfTbxD6gsYoGXJ_Dn0KTMyeGIKwS2HEDaA,952
25
+ src/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ src/utils/click_group.py,sha256=Dn0QJWHni4_9-j0tpFmYAXH5z3ooq1h52eBBxzbXhvE,571
27
+ src/utils/config_utils.py,sha256=SV0SVLzffJtV1rqV1gAn5kbYXi_OGca_lbTiCOMtCFQ,2718
28
+ src/utils/message_utils.py,sha256=U6zkcrMCCdXW-VAEHF6BMKdSkxGvVpsSHriQDzpWL38,696
29
+ src/utils/prepare_headers.py,sha256=5eiun_bAZ2JzrQyr2ZtZthSp0j4WU-m4DSjr_F2KPPc,2528
30
+ src/utils/response_utils.py,sha256=r4fiTCANLDMCC2eElQKcguKkaJ7AnU6_1x_bai5uaEA,849
31
+ src/utils/version_control.py,sha256=D0I3DnonnnMtXxJTGAHiM7aNgPR277CrRLMwfbTpjqY,2032
32
+ src/utils/consts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
+ src/utils/consts/message_consts.py,sha256=6MTsYRw5qwUZLVg5mt84sE3ZZOTRH9YdcT9JubPi_k0,654
34
+ src/utils/consts/telemetry_consts.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
+ src/utils/cryptography/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
+ src/utils/cryptography/aes_crypto.py,sha256=6x5Z_sPRlTVCSDCLg77LOcuUFP4oY1buTiYxhM9dXJ0,3433
37
+ src/utils/cryptography/encryption_module.py,sha256=oZgMqxwltQHSMgxS-Kc21gzvMbIIu1int0toRgI5E5I,2013
38
+ src/utils/cryptography/rsa_crypto.py,sha256=2g6IVe_UMZ0w_YpT6AWC3qwOepAK9MmXxjOAdXZ15Cc,4306
39
+ cgcsdk-0.1.10.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
+ cgcsdk-0.1.10.dist-info/METADATA,sha256=FZMRrBLJl52bRbo_p3Xb2ebpUDAmr-GZeLr39IJ7ElM,1107
41
+ cgcsdk-0.1.10.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
42
+ cgcsdk-0.1.10.dist-info/entry_points.txt,sha256=uaM2l0xK1cv3U7jjZqsHQCL-YdYyGKd5GnzP5ykvL9Q,36
43
+ cgcsdk-0.1.10.dist-info/top_level.txt,sha256=qixLkg2QFPjkS6Q4Af7kQ7NywvH_H33BAB7KVCyEy4w,12
44
+ cgcsdk-0.1.10.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.37.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ cgc = src.cgc:cli
@@ -0,0 +1,2 @@
1
+ src
2
+ src/cgc
src/.env ADDED
@@ -0,0 +1,9 @@
1
+ API_HOST=172.16.246.2
2
+ API_PORT=21919
3
+ BILLING_PORT=21920
4
+ CGC_SECRET=2e90ba40-15c1-4d2e-9d85-df4940e3c915
5
+ CONFIG_FILE_NAME = cfg.json
6
+ TMP_DIR = .tmp
7
+ MAJOR_VERSION = 1
8
+ MINOR_VERSION = 0
9
+
src/__init__.py ADDED
@@ -0,0 +1,9 @@
1
+ """
2
+ cgc
3
+ Comtegra GPU Cloud REST API client
4
+ """
5
+ from src.utils.version_control import get_version
6
+
7
+ __version__ = get_version()
8
+ __author__ = "Przemysław Kowalski <przemyslaw.kowalski@comtegra.pl>, Jakub Zboina <jakub.zboina@comtegra.pl>"
9
+ __credits__ = "Quantum Dev Labs"
src/cgc.py ADDED
@@ -0,0 +1,30 @@
1
+ import click
2
+
3
+ from src.commands.volume.volume_cmd import volume_group
4
+ from src.commands.compute.compute_cmd import compute_group
5
+ from src.commands.billing.billing_cmd import billing_group
6
+ from src.commands.auth.auth_cmd import auth_register
7
+ from src.commands.cgc_cmd import cgc_rm
8
+ from src.utils.version_control import get_version
9
+ from src.utils.click_group import CustomGroup
10
+
11
+
12
+ @click.group(cls=CustomGroup)
13
+ @click.version_option(get_version())
14
+ def cli():
15
+
16
+ """CGC application developed by Comtegra S.A."""
17
+ pass
18
+
19
+
20
+ cli.add_command(volume_group)
21
+ cli.add_command(compute_group)
22
+ cli.add_command(billing_group)
23
+ cli.add_command(auth_register)
24
+ cli.add_command(cgc_rm)
25
+
26
+
27
+ if __name__ == "__main__" or __name__ == "src.cgc":
28
+ cli()
29
+ else:
30
+ raise Exception("This program is not intended for importing!")
File without changes
File without changes
@@ -0,0 +1,70 @@
1
+ import click
2
+ import requests
3
+ import os
4
+ import shutil
5
+ import pkg_resources
6
+ from dotenv import load_dotenv
7
+
8
+ from src.utils.config_utils import get_config_path, get_namespace
9
+ from src.commands.auth import auth_utils
10
+ from src.utils.cryptography import rsa_crypto
11
+ from src.utils.config_utils import add_to_config
12
+ from src.utils.prepare_headers import get_api_url_and_prepare_headers_register
13
+ from src.telemetry.basic import increment_metric
14
+ from src.utils.message_utils import prepare_error_message, prepare_success_message
15
+ from src.utils.consts.message_consts import TIMEOUT_ERROR
16
+ from src.utils.click_group import CustomCommand
17
+
18
+ ENV_FILE_PATH = pkg_resources.resource_filename("src", ".env")
19
+ load_dotenv(dotenv_path=ENV_FILE_PATH, verbose=True)
20
+ TMP_DIR = os.path.join(get_config_path(), os.getenv("TMP_DIR"))
21
+
22
+
23
+ @click.command("register", cls=CustomCommand)
24
+ @click.option("--user_id", "-u", "user_id", prompt=True)
25
+ @click.option("--access_key", "-k", "access_key", prompt=True)
26
+ def auth_register(user_id: str, access_key: str):
27
+ """Register a user in system using user id and access key.
28
+ \f
29
+ :param user_id: username received in invite
30
+ :type user_id: str
31
+ :param access_key: access key received in invite
32
+ :type access_key: str
33
+ """
34
+
35
+ url, headers = get_api_url_and_prepare_headers_register(user_id, access_key)
36
+ pub_key_bytes, priv_key_bytes = rsa_crypto.key_generate_pair()
37
+ payload = pub_key_bytes
38
+
39
+ try:
40
+ res = requests.post(
41
+ url,
42
+ data=payload,
43
+ headers=headers,
44
+ allow_redirects=True,
45
+ timeout=10,
46
+ )
47
+
48
+ if res.status_code != 200:
49
+ increment_metric("auth.register.error")
50
+ if res.status_code == 401:
51
+ message = "Could not validate user id or access key."
52
+ click.echo(prepare_error_message(message))
53
+ return
54
+
55
+ message = "Could not register user. Please try again or contact support at support@comtegra.pl."
56
+ click.echo(prepare_error_message(message))
57
+ return
58
+
59
+ unzip_dir = auth_utils.save_and_unzip_file(res)
60
+ aes_key, passwd = auth_utils.get_aes_key_and_passwd(unzip_dir, priv_key_bytes)
61
+
62
+ add_to_config(user_id=user_id, passwd=passwd, aes_key=aes_key)
63
+ auth_utils.auth_create_api_key()
64
+
65
+ message = f"Register successful! You can now use the CLI. Config file is saved in {get_config_path()}"
66
+ click.echo(prepare_success_message(message))
67
+ increment_metric(f"{get_namespace()}.auth.register.ok")
68
+ shutil.rmtree(TMP_DIR)
69
+ except requests.exceptions.ReadTimeout:
70
+ click.echo(prepare_error_message(TIMEOUT_ERROR))
@@ -0,0 +1,151 @@
1
+ import click, requests, json, os, shutil, glob, base64, urllib, pkg_resources
2
+ from dotenv import load_dotenv
3
+
4
+ from src.utils.config_utils import get_config_path, get_namespace
5
+ from src.telemetry.basic import increment_metric
6
+ from src.utils.config_utils import add_to_config
7
+ from src.utils.config_utils import read_from_cfg
8
+ from src.utils.cryptography import rsa_crypto
9
+ from src.utils import prepare_headers
10
+ from src.utils.message_utils import prepare_error_message
11
+
12
+ ENV_FILE_PATH = pkg_resources.resource_filename("src", ".env")
13
+ load_dotenv(dotenv_path=ENV_FILE_PATH, verbose=True)
14
+
15
+ API_HOST = os.getenv("API_HOST")
16
+ API_PORT = os.getenv("API_PORT")
17
+ API_URL = f"http://{API_HOST}:{API_PORT}"
18
+ CGC_SECRET = os.getenv("CGC_SECRET")
19
+ TMP_DIR = os.path.join(get_config_path(), os.getenv("TMP_DIR"))
20
+
21
+
22
+ def get_jwt() -> str:
23
+ """Command to get JWT token and api key for user
24
+
25
+ :param username: _description_
26
+ :type username: str
27
+ :param password: _description_
28
+ :type password: str
29
+ """
30
+
31
+ user_id = urllib.parse.quote(read_from_cfg("user_id"))
32
+ passwd = urllib.parse.quote(read_from_cfg("passwd"))
33
+ url = f"{API_URL}/v1/api/user/create/token"
34
+ payload = f"grant_type=&username={user_id}&password={passwd}"
35
+
36
+ jwt_headers = {
37
+ "accept": "application/json",
38
+ "Content-Type": "application/x-www-form-urlencoded",
39
+ }
40
+ try:
41
+ res = requests.post(
42
+ url,
43
+ payload,
44
+ headers=jwt_headers,
45
+ timeout=10,
46
+ )
47
+
48
+ if res.status_code != 200:
49
+ message = (
50
+ "Could not generate JWT. Try again or contact us at support@comtegra.pl"
51
+ )
52
+ click.echo(prepare_error_message(message))
53
+ increment_metric(f"{get_namespace()}.auth.jwt.error")
54
+ return None
55
+ increment_metric(f"{get_namespace()}.auth.jwt.ok")
56
+
57
+ data = json.loads(res.text)
58
+ jwt = data["access_token"]
59
+ return jwt
60
+
61
+ except requests.exceptions.ReadTimeout:
62
+ message = "Connection timed out. Try again or contact us at support@comtegra.pl"
63
+ click.echo(prepare_error_message(message))
64
+
65
+
66
+ def auth_create_api_key():
67
+ """Command to create API key and API secret for user and save it to config file."""
68
+ url = f"{API_URL}/v1/api/user/create/api-key"
69
+ headers = prepare_headers.prepare_headers_api_key()
70
+ try:
71
+ res = requests.post(
72
+ url,
73
+ headers=headers,
74
+ timeout=10,
75
+ )
76
+
77
+ if res.status_code != 200:
78
+ message = "Could not generate API key. Try again or contact us at support@comtegra.pl"
79
+ click.echo(prepare_error_message(message))
80
+ increment_metric(f"{get_namespace()}.auth.api-key.error")
81
+ return
82
+ increment_metric(f"{get_namespace()}.auth.api-key.ok")
83
+
84
+ data = json.loads(res.text)
85
+
86
+ api_key = data["details"].get("_id")
87
+ secret = data["details"].get("secret")
88
+
89
+ add_to_config(api_key=api_key, api_secret=secret)
90
+
91
+ except requests.exceptions.ReadTimeout:
92
+ message = "Connection timed out. Try again or contact us at support@comtegra.pl"
93
+ click.echo(prepare_error_message(message))
94
+
95
+
96
+ def save_and_unzip_file(res: requests.Response) -> str:
97
+ """Command to save file, unzip it and return path to its directory.
98
+
99
+ :param res: API response with file to save and unzip
100
+ :type res: requests.Response
101
+ :return: path to the directory containing the file
102
+ :rtype: str
103
+ """
104
+ zip_file = res.headers.get("content-disposition").split('"')[1]
105
+ namespace = zip_file.split("---")[-1].split(".")[0]
106
+ add_to_config(namespace=namespace)
107
+
108
+ if not os.path.isdir(TMP_DIR):
109
+ os.makedirs(TMP_DIR)
110
+ zip_file_path = f"{TMP_DIR}/{zip_file}"
111
+ with open(zip_file_path, "wb") as f:
112
+ f.write(res.content)
113
+
114
+ unzip_dir = zip_file_path[:-4]
115
+ shutil.unpack_archive(zip_file_path, unzip_dir)
116
+
117
+ return unzip_dir
118
+
119
+
120
+ def get_aes_key_and_passwd(unzip_dir: str, priv_key_bytes: bytes):
121
+ """Command to get AES key and password from the unzipped file.
122
+
123
+ :param unzip_dir: path to the directory containing the file
124
+ :type unzip_dir: str
125
+ :param priv_key_bytes: private key bytes
126
+ :type priv_key_bytes: bytes
127
+ :return: AES key and password
128
+ :rtype: str, str
129
+ """
130
+ encrypted_passwd_path = ""
131
+ encrypted_aes_path = ""
132
+ for file in glob.glob(
133
+ f"{unzip_dir}/**/*encrypted*",
134
+ recursive=True,
135
+ ):
136
+ if file.endswith("priv"):
137
+ encrypted_aes_path = f"{file}"
138
+ elif file.endswith("password"):
139
+ encrypted_passwd_path = f"{file}"
140
+
141
+ rsa_key = rsa_crypto.import_create_RSAKey(priv_key_bytes)
142
+
143
+ with open(encrypted_aes_path, "rb") as aes, open(
144
+ encrypted_passwd_path, "rb"
145
+ ) as pwd:
146
+ aes_key = rsa_crypto.decrypt_rsa(aes.read(), rsa_key).decode("ascii")
147
+ passwd = base64.b64decode(rsa_crypto.decrypt_rsa(pwd.read(), rsa_key)).decode(
148
+ "utf-8"
149
+ )
150
+
151
+ return aes_key, passwd
File without changes
@@ -0,0 +1,115 @@
1
+ import os
2
+ import click
3
+ import pkg_resources
4
+ import requests
5
+ from tabulate import tabulate
6
+ from dotenv import load_dotenv
7
+
8
+
9
+ from src.commands.billing.billing_utils import (
10
+ get_status_list_to_print,
11
+ get_status_list_headers,
12
+ )
13
+ from src.telemetry.basic import increment_metric
14
+ from src.utils.config_utils import get_namespace
15
+ from src.utils.message_utils import prepare_error_message
16
+ from src.utils.prepare_headers import get_api_url_and_prepare_headers
17
+ from src.utils.response_utils import response_precheck
18
+ from src.utils.click_group import CustomGroup, CustomCommand
19
+
20
+ ENV_FILE_PATH = pkg_resources.resource_filename("src", ".env")
21
+ load_dotenv(dotenv_path=ENV_FILE_PATH, verbose=True)
22
+
23
+
24
+ API_URL = os.getenv("API_URL")
25
+ CGC_SECRET = os.getenv("CGC_SECRET")
26
+
27
+
28
+ @click.group("billing", cls=CustomGroup)
29
+ def billing_group():
30
+ """
31
+ Access and manage billing information.
32
+ """
33
+ pass
34
+
35
+
36
+ @billing_group.command("status", cls=CustomCommand)
37
+ def billing_status():
38
+ """
39
+ Shows billing status for user namespace
40
+ """
41
+ metric_ok = f"{get_namespace()}.billing.status.ok"
42
+ metric_error = f"{get_namespace()}.billing.status.error"
43
+
44
+ api_url, headers = get_api_url_and_prepare_headers()
45
+ url = f"{api_url}/v1/api/billing/status"
46
+ try:
47
+ response = requests.get(
48
+ url=url,
49
+ headers=headers,
50
+ timeout=10,
51
+ )
52
+
53
+ response_precheck(response, metric_error)
54
+
55
+ if response.status_code != 200:
56
+ increment_metric(metric_error)
57
+ message = "Error occuerd while getting billing status. Try again or contact us at support@comtegra.pl"
58
+ click.echo(prepare_error_message(message))
59
+ return
60
+
61
+ data = response.json()
62
+ total_cost = data["details"]["costs"]["cost_total"]
63
+ namespace = data["details"]["namespace"]
64
+ costs_list = data["details"]["costs"]["details"]
65
+ if not costs_list:
66
+ click.echo("No costs found")
67
+ return
68
+
69
+ costs_list_to_print = get_status_list_to_print(costs_list)
70
+ list_headers = get_status_list_headers()
71
+ click.echo(tabulate(costs_list_to_print, headers=list_headers))
72
+ click.echo(f"\nTotal cost for namespace {namespace}: {total_cost:.2f}\n")
73
+ increment_metric(metric_ok)
74
+ except requests.exceptions.ReadTimeout:
75
+ message = "Connection timed out. Try again or contact us at support@comtegra.pl"
76
+ increment_metric(metric_error)
77
+ click.echo(prepare_error_message(message))
78
+
79
+
80
+ @billing_group.command("pay", cls=CustomCommand)
81
+ def billing_pay():
82
+ """
83
+ Interface for easy payment
84
+ """
85
+ click.echo("Initializing payment!")
86
+
87
+
88
+ @click.group("fvat", cls=CustomGroup)
89
+ def fvat_group():
90
+ """
91
+ Invoice management
92
+ """
93
+ pass
94
+
95
+
96
+ @fvat_group.command("ls", cls=CustomCommand)
97
+ def fvat_ls():
98
+ """
99
+ Lists all invoices for user namespace
100
+ """
101
+ click.echo("Listing all invoices!")
102
+
103
+
104
+ @fvat_group.command("id", cls=CustomCommand)
105
+ @click.argument(
106
+ "invoice_id",
107
+ )
108
+ def fvat_id(invoice_id: str):
109
+ """
110
+ Opens invoice with given ID
111
+ """
112
+ click.echo(f"Opening invoice {invoice_id}!")
113
+
114
+
115
+ billing_group.add_command(fvat_group)
@@ -0,0 +1,36 @@
1
+ def get_status_list_to_print(costs_list: list):
2
+ """Method format data in list of voulmes to be displayed in table and calculate total size of volumes
3
+
4
+ :param list_of_volumes: list of volumes to be formatted
5
+ :type list_of_volumes: list
6
+ :return: list of volumes to be displayed in table and total size of volumes
7
+ :rtype: list, int
8
+ """
9
+ list_to_print = []
10
+ for cost in costs_list:
11
+ rent_start = cost["rent_start"]
12
+ rent_end = cost["rent_end"]
13
+ rent_time = f"{int(cost['rent_time'])} s"
14
+
15
+ total_cost = f"{float(cost['cost_total']):.2f} pln"
16
+ resources = cost["resources"]
17
+ name = resources["name"]
18
+ rent_type = cost["type"]
19
+ if rent_type == "events_compute":
20
+ entity = resources["entity"]
21
+ elif rent_type == "events_volume":
22
+ entity = "volume"
23
+ row_list = [entity, name, rent_start, rent_end, rent_time, total_cost]
24
+ list_to_print.append(row_list)
25
+ list_to_print.sort(key=lambda d: f"{d[0]} {d[2]}")
26
+ return list_to_print
27
+
28
+
29
+ def get_status_list_headers():
30
+ """Generates headers for billing status command
31
+
32
+ :return: list of headers
33
+ :rtype: list
34
+ """
35
+ headers = ["entity", "name", "start", "end", "time", "cost"]
36
+ return headers
@@ -0,0 +1,18 @@
1
+ import click
2
+
3
+ from src.commands.compute.compute_cmd import compute_delete
4
+ from src.utils.version_control import check_version
5
+ from src.utils.click_group import CustomCommand
6
+
7
+
8
+ @click.command("rm", cls=CustomCommand)
9
+ @click.argument("name", type=click.STRING)
10
+ def cgc_rm(name: str):
11
+ """
12
+ Delete a compute pod in user namespace
13
+ \f
14
+ :param name: name of pod to delete
15
+ :type name: str
16
+ """
17
+ check_version()
18
+ compute_delete(name)
File without changes