twc-cli 2.6.0__py3-none-any.whl → 2.8.0__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.
Potentially problematic release.
This version of twc-cli might be problematic. Click here for more details.
- CHANGELOG.md +21 -0
- twc/__main__.py +2 -0
- twc/__version__.py +1 -1
- twc/api/client.py +18 -7
- twc/apiwrap.py +6 -1
- twc/commands/__init__.py +1 -1
- twc/commands/account.py +30 -0
- twc/commands/domain.py +79 -16
- twc/commands/project.py +2 -8
- twc/commands/server.py +1 -3
- twc/commands/storage.py +6 -7
- twc/fmt.py +11 -4
- twc/vars.py +1 -1
- {twc_cli-2.6.0.dist-info → twc_cli-2.8.0.dist-info}/METADATA +2 -1
- {twc_cli-2.6.0.dist-info → twc_cli-2.8.0.dist-info}/RECORD +18 -18
- {twc_cli-2.6.0.dist-info → twc_cli-2.8.0.dist-info}/WHEEL +1 -1
- {twc_cli-2.6.0.dist-info → twc_cli-2.8.0.dist-info}/COPYING +0 -0
- {twc_cli-2.6.0.dist-info → twc_cli-2.8.0.dist-info}/entry_points.txt +0 -0
CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
В этом файле описаны все значимые изменения в Timeweb Cloud CLI. В выпусках мы придерживается правил [семантического версионирования](https://semver.org/lang/ru/).
|
|
4
4
|
|
|
5
|
+
# Версия 2.7.0 (2024.11.02)
|
|
6
|
+
|
|
7
|
+
## Добавлено
|
|
8
|
+
|
|
9
|
+
- Добавлена новая команда `twc whoami`, которая показывает логин аккаунта, в котором в текущий момент авторизован CLI.
|
|
10
|
+
|
|
11
|
+
## Изменено
|
|
12
|
+
|
|
13
|
+
- Команда `twc account status` теперь также отображает логин аккаунта.
|
|
14
|
+
|
|
15
|
+
## Исправлено
|
|
16
|
+
|
|
17
|
+
- Устранено падение программы при листинге образов (`twc image list`) и при создании сервера из образа.
|
|
18
|
+
- Исправлена ошибка при создании сервера с приватной сетью в зоне доступности SPB-3.
|
|
19
|
+
- Исправлены ошибки в командах `twc storage subdomain gencert` и `twc storage subdomain remove`, возникшие из-за нарушения обратной совместимости эндпоинтов API.
|
|
20
|
+
- Исправлена ошибка валидации приватного адреса при созаднии сервера — ошибочно запрещалось использовать 4-й по порядку адрес в подсети.
|
|
21
|
+
- Исправлена ошибка разбора JSON при выводе списка ресурсов в проекте.
|
|
22
|
+
- Исправлены ошибки добавления TXT записи для домена в команде `twc domain record add`.
|
|
23
|
+
- Исправлена ошибка парсинка субдомена в команде `twc domain record add`.
|
|
24
|
+
- Исправлены ошибки обновления DNS-записей на поддоменах.
|
|
25
|
+
|
|
5
26
|
# Версия 2.6.0 (2024.08.14)
|
|
6
27
|
|
|
7
28
|
## Добавлено
|
twc/__main__.py
CHANGED
|
@@ -21,6 +21,7 @@ from .commands import (
|
|
|
21
21
|
vpc,
|
|
22
22
|
firewall,
|
|
23
23
|
floating_ip,
|
|
24
|
+
whoami,
|
|
24
25
|
)
|
|
25
26
|
from .commands.common import version_callback, version_option, verbose_option
|
|
26
27
|
|
|
@@ -46,6 +47,7 @@ cli.add_typer(domain, name="domain", aliases=["domains", "d"])
|
|
|
46
47
|
cli.add_typer(vpc, name="vpc", aliases=["vpcs", "network", "networks"])
|
|
47
48
|
cli.add_typer(firewall, name="firewall", aliases=["fw"])
|
|
48
49
|
cli.add_typer(floating_ip, name="ip", aliases=["ips"])
|
|
50
|
+
cli.add_typer(whoami, name="whoami", aliases=[])
|
|
49
51
|
|
|
50
52
|
|
|
51
53
|
@cli.command("version")
|
twc/__version__.py
CHANGED
twc/api/client.py
CHANGED
|
@@ -469,14 +469,11 @@ class TimewebCloud(TimewebCloudBase):
|
|
|
469
469
|
# -----------------------------------------------------------------------
|
|
470
470
|
# Images
|
|
471
471
|
|
|
472
|
-
def get_images(
|
|
473
|
-
self, limit: int = 100, offset: int = 0, with_deleted: bool = False
|
|
474
|
-
):
|
|
472
|
+
def get_images(self, limit: int = 100, offset: int = 0):
|
|
475
473
|
"""Get list of images."""
|
|
476
474
|
params = {
|
|
477
475
|
"limit": limit,
|
|
478
476
|
"offset": offset,
|
|
479
|
-
"with_deleted": with_deleted,
|
|
480
477
|
}
|
|
481
478
|
return self._request("GET", f"{self.api_url}/images", params=params)
|
|
482
479
|
|
|
@@ -1413,17 +1410,31 @@ class TimewebCloud(TimewebCloudBase):
|
|
|
1413
1410
|
self,
|
|
1414
1411
|
fqdn: str,
|
|
1415
1412
|
dns_record_type: DNSRecordType,
|
|
1416
|
-
value: str,
|
|
1413
|
+
value: Optional[str] = None,
|
|
1417
1414
|
subdomain: Optional[str] = None,
|
|
1418
1415
|
priority: Optional[int] = None,
|
|
1416
|
+
ttl: Optional[int] = None,
|
|
1417
|
+
protocol: Optional[str] = None,
|
|
1418
|
+
service: Optional[str] = None,
|
|
1419
|
+
host: Optional[str] = None,
|
|
1420
|
+
port: Optional[int] = None,
|
|
1421
|
+
*,
|
|
1422
|
+
null_subdomain: bool = False,
|
|
1419
1423
|
):
|
|
1420
1424
|
"""Add DNS record to domain."""
|
|
1421
1425
|
payload = {
|
|
1422
1426
|
"type": dns_record_type,
|
|
1423
|
-
"value": value,
|
|
1427
|
+
**({"value": value} if value else {}),
|
|
1424
1428
|
**({"subdomain": subdomain} if subdomain else {}),
|
|
1425
|
-
**({"priority": priority} if priority else {}),
|
|
1429
|
+
**({"priority": priority} if priority is not None else {}),
|
|
1430
|
+
**({"ttl": ttl} if ttl else {}),
|
|
1431
|
+
**({"protocol": protocol} if protocol else {}),
|
|
1432
|
+
**({"service": service} if service else {}),
|
|
1433
|
+
**({"host": host} if host else {}),
|
|
1434
|
+
**({"port": port} if port else {}),
|
|
1426
1435
|
}
|
|
1436
|
+
if null_subdomain:
|
|
1437
|
+
payload["subdomain"] = None
|
|
1427
1438
|
return self._request(
|
|
1428
1439
|
"POST",
|
|
1429
1440
|
f"{self.api_url}/domains/{fqdn}/dns-records",
|
twc/apiwrap.py
CHANGED
|
@@ -4,7 +4,7 @@ import os
|
|
|
4
4
|
import sys
|
|
5
5
|
import textwrap
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from logging import debug
|
|
7
|
+
from logging import debug, warning
|
|
8
8
|
|
|
9
9
|
from .api import TimewebCloud
|
|
10
10
|
from .api import exceptions as exc
|
|
@@ -70,6 +70,11 @@ def create_client(config: Path, profile: str, **kwargs) -> TimewebCloud:
|
|
|
70
70
|
"""
|
|
71
71
|
token = os.getenv("TWC_TOKEN")
|
|
72
72
|
log_settings = os.getenv("TWC_LOG")
|
|
73
|
+
api_endpoint = os.getenv("TWC_ENDPOINT")
|
|
74
|
+
|
|
75
|
+
if api_endpoint:
|
|
76
|
+
warning("Using API URL from environment: %s", api_endpoint)
|
|
77
|
+
kwargs["api_base_url"] = api_endpoint
|
|
73
78
|
|
|
74
79
|
if log_settings:
|
|
75
80
|
for param in log_settings.split(","):
|
twc/commands/__init__.py
CHANGED
twc/commands/account.py
CHANGED
|
@@ -18,6 +18,35 @@ from .common import (
|
|
|
18
18
|
)
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
whoami = TyperAlias(help="Display current login.", no_args_is_help=False)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# ------------------------------------------------------------- #
|
|
25
|
+
# $ twc whoami #
|
|
26
|
+
# ------------------------------------------------------------- #
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@whoami.callback(invoke_without_command=True)
|
|
30
|
+
def whoami_callback(
|
|
31
|
+
verbose: Optional[bool] = verbose_option,
|
|
32
|
+
config: Optional[Path] = config_option,
|
|
33
|
+
profile: Optional[str] = profile_option,
|
|
34
|
+
output_format: Optional[str] = output_format_option,
|
|
35
|
+
):
|
|
36
|
+
"""Display current login."""
|
|
37
|
+
client = create_client(config, profile)
|
|
38
|
+
response = client.get_account_status()
|
|
39
|
+
login = response.json()["status"]["login"]
|
|
40
|
+
fmt.printer(
|
|
41
|
+
{"login": login, "profile": profile},
|
|
42
|
+
output_format=output_format,
|
|
43
|
+
func=lambda data: print(data["login"]),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# ------------------------------------------------------------- #
|
|
48
|
+
|
|
49
|
+
|
|
21
50
|
account = TyperAlias(help=__doc__)
|
|
22
51
|
account_access = TyperAlias(help="Manage account access restrictions.")
|
|
23
52
|
account.add_typer(account_access, name="access")
|
|
@@ -33,6 +62,7 @@ def print_account_status(response: Response):
|
|
|
33
62
|
status = response.json()["status"]
|
|
34
63
|
output = dedent(
|
|
35
64
|
f"""
|
|
65
|
+
Login: {status["login"]}
|
|
36
66
|
Provider: {status["company_info"]["name"]}
|
|
37
67
|
Yandex.Metrika ID: {status["ym_client_id"]}
|
|
38
68
|
Blocked: {status["is_blocked"]}
|
twc/commands/domain.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
import sys
|
|
5
|
+
from enum import Enum
|
|
5
6
|
from typing import Optional, List
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
|
|
@@ -195,7 +196,7 @@ def domain_delete(
|
|
|
195
196
|
# ------------------------------------------------------------- #
|
|
196
197
|
|
|
197
198
|
|
|
198
|
-
@domain.command("add")
|
|
199
|
+
@domain.command("add", "create")
|
|
199
200
|
def domain_add(
|
|
200
201
|
domain_name: str,
|
|
201
202
|
verbose: Optional[bool] = verbose_option,
|
|
@@ -316,7 +317,15 @@ def domain_remove_dns_record(
|
|
|
316
317
|
# ------------------------------------------------------------- #
|
|
317
318
|
|
|
318
319
|
|
|
319
|
-
|
|
320
|
+
class SRVProtocol(str, Enum):
|
|
321
|
+
"""Supported protocols for SRV records."""
|
|
322
|
+
|
|
323
|
+
TCP = "TCP"
|
|
324
|
+
UPD = "UDP"
|
|
325
|
+
TLS = "TLS"
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
@domain_record.command("add", "create")
|
|
320
329
|
def domain_add_dns_record(
|
|
321
330
|
domain_name: str,
|
|
322
331
|
verbose: Optional[bool] = verbose_option,
|
|
@@ -331,12 +340,40 @@ def domain_add_dns_record(
|
|
|
331
340
|
metavar="TYPE",
|
|
332
341
|
help=f"[{'|'.join([k.value for k in DNSRecordType])}]",
|
|
333
342
|
),
|
|
334
|
-
value: Optional[str] = typer.Option(
|
|
343
|
+
value: Optional[str] = typer.Option(
|
|
344
|
+
None,
|
|
345
|
+
help="Record value. Skip it for SRV records.",
|
|
346
|
+
),
|
|
335
347
|
priority: Optional[int] = typer.Option(
|
|
336
348
|
None,
|
|
337
349
|
"--prio",
|
|
338
350
|
help="Record priority. Supported for MX, SRV records.",
|
|
339
351
|
),
|
|
352
|
+
service: Optional[str] = typer.Option(
|
|
353
|
+
None,
|
|
354
|
+
"--service",
|
|
355
|
+
help="Service for SRV record e.g '_matrix'.",
|
|
356
|
+
),
|
|
357
|
+
proto: Optional[SRVProtocol] = typer.Option(
|
|
358
|
+
None,
|
|
359
|
+
"--proto",
|
|
360
|
+
help="Protocol for SRV record.",
|
|
361
|
+
),
|
|
362
|
+
host: Optional[str] = typer.Option(
|
|
363
|
+
None,
|
|
364
|
+
"--host",
|
|
365
|
+
help="Host for SRV record.",
|
|
366
|
+
),
|
|
367
|
+
port: Optional[int] = typer.Option(
|
|
368
|
+
None,
|
|
369
|
+
"--port",
|
|
370
|
+
help="Port for SRV record.",
|
|
371
|
+
min=1,
|
|
372
|
+
max=65535,
|
|
373
|
+
),
|
|
374
|
+
ttl: Optional[int] = typer.Option(
|
|
375
|
+
None, "--ttl", help="Time-To-Live for DNS record."
|
|
376
|
+
),
|
|
340
377
|
second_ld: Optional[bool] = typer.Option(
|
|
341
378
|
False,
|
|
342
379
|
"--2ld",
|
|
@@ -346,30 +383,50 @@ def domain_add_dns_record(
|
|
|
346
383
|
"""Add dns record for domain or subdomain."""
|
|
347
384
|
client = create_client(config, profile)
|
|
348
385
|
|
|
386
|
+
if record_type != "SRV" and not value:
|
|
387
|
+
sys.exit("Error: --value is expected for non-SRV DNS records")
|
|
388
|
+
|
|
389
|
+
null_subdomain = False
|
|
390
|
+
|
|
349
391
|
if second_ld:
|
|
350
392
|
offset = 3
|
|
351
393
|
else:
|
|
352
394
|
offset = 2
|
|
353
395
|
|
|
354
396
|
subdomain = domain_name
|
|
397
|
+
original_domain_name = domain_name
|
|
355
398
|
domain_name = ".".join(domain_name.split(".")[-offset:])
|
|
356
399
|
|
|
357
400
|
if subdomain == domain_name:
|
|
358
401
|
subdomain = None
|
|
359
402
|
|
|
360
|
-
# API issue: see text below
|
|
361
|
-
# API can add TXT record (only TXT, why?) with non-existent subdomain,
|
|
362
|
-
# but 'subdomain' option must not be passed as FQDN!
|
|
363
|
-
# API issue: You cannot create subdomains with underscore. Why?
|
|
364
|
-
# Use previous described bug for this! Pass your subdomain with
|
|
365
|
-
# underscores to this function.
|
|
366
403
|
if record_type.lower() == "txt":
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
404
|
+
if subdomain is None:
|
|
405
|
+
null_subdomain = True
|
|
406
|
+
else:
|
|
407
|
+
# 'ftp.example.org' --> 'ftp'
|
|
408
|
+
subdomain = ".".join(subdomain.split(".")[:-offset])
|
|
409
|
+
else:
|
|
410
|
+
subdomain = ".".join(original_domain_name.split(".")[:-offset])
|
|
411
|
+
if subdomain != "":
|
|
412
|
+
domain_name = original_domain_name
|
|
413
|
+
subdomain = None
|
|
414
|
+
|
|
415
|
+
payload = {
|
|
416
|
+
"fqdn": domain_name,
|
|
417
|
+
"dns_record_type": record_type,
|
|
418
|
+
"value": value,
|
|
419
|
+
"subdomain": subdomain,
|
|
420
|
+
"priority": priority,
|
|
421
|
+
"ttl": ttl,
|
|
422
|
+
"protocol": "_" + proto if proto else None,
|
|
423
|
+
"service": service,
|
|
424
|
+
"host": host,
|
|
425
|
+
"port": port,
|
|
426
|
+
"null_subdomain": null_subdomain,
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
response = client.add_domain_dns_record(**payload)
|
|
373
430
|
fmt.printer(
|
|
374
431
|
response,
|
|
375
432
|
output_format=output_format,
|
|
@@ -424,6 +481,12 @@ def domain_update_dns_records(
|
|
|
424
481
|
if subdomain == domain_name:
|
|
425
482
|
subdomain = None
|
|
426
483
|
|
|
484
|
+
if record_type.lower() == "txt" and subdomain is not None:
|
|
485
|
+
subdomain = ".".join(subdomain.split(".")[:-offset])
|
|
486
|
+
elif subdomain is not None:
|
|
487
|
+
domain_name = subdomain
|
|
488
|
+
subdomain = None
|
|
489
|
+
|
|
427
490
|
response = client.update_domain_dns_record(
|
|
428
491
|
domain_name, record_id, record_type, value, subdomain, priority
|
|
429
492
|
)
|
|
@@ -439,7 +502,7 @@ def domain_update_dns_records(
|
|
|
439
502
|
# ------------------------------------------------------------- #
|
|
440
503
|
|
|
441
504
|
|
|
442
|
-
@domain_subdomain.command("add")
|
|
505
|
+
@domain_subdomain.command("add", "create")
|
|
443
506
|
def domain_add_subdomain(
|
|
444
507
|
subdomain: str = typer.Argument(..., metavar="FQDN"),
|
|
445
508
|
verbose: Optional[bool] = verbose_option,
|
twc/commands/project.py
CHANGED
|
@@ -188,17 +188,11 @@ def print_resources(response: Response):
|
|
|
188
188
|
for key in resource_keys:
|
|
189
189
|
if resources[key]:
|
|
190
190
|
for resource in resources[key]:
|
|
191
|
-
try:
|
|
192
|
-
location = resource["location"]
|
|
193
|
-
except KeyError:
|
|
194
|
-
# Balancers, clusters, and databases has no 'location' field.
|
|
195
|
-
# These services is available only in 'ru-1' location.
|
|
196
|
-
location = "ru-1"
|
|
197
191
|
table.row(
|
|
198
192
|
[
|
|
199
193
|
resource["id"],
|
|
200
|
-
resource
|
|
201
|
-
location,
|
|
194
|
+
resource.get("name", resource.get("fqdn")),
|
|
195
|
+
resource.get("location", "ru-1"),
|
|
202
196
|
key[:-1], # this is resource name e.g. 'server'
|
|
203
197
|
]
|
|
204
198
|
)
|
twc/commands/server.py
CHANGED
|
@@ -584,9 +584,7 @@ def server_create(
|
|
|
584
584
|
net = IPv4Network(
|
|
585
585
|
client.get_vpc(network).json()["vpc"]["subnet_v4"]
|
|
586
586
|
)
|
|
587
|
-
if IPv4Address(private_ip)
|
|
588
|
-
int(net.network_address) + 4
|
|
589
|
-
):
|
|
587
|
+
if IPv4Address(private_ip) >= net.network_address + 4:
|
|
590
588
|
payload["network"]["ip"] = private_ip
|
|
591
589
|
else:
|
|
592
590
|
# First 3 addresses is reserved for networks OVN based networks
|
twc/commands/storage.py
CHANGED
|
@@ -500,7 +500,6 @@ def storage_subdomain_remove(
|
|
|
500
500
|
verbose: Optional[bool] = verbose_option,
|
|
501
501
|
config: Optional[Path] = config_option,
|
|
502
502
|
profile: Optional[str] = profile_option,
|
|
503
|
-
output_format: Optional[str] = output_format_option,
|
|
504
503
|
yes: Optional[bool] = yes_option,
|
|
505
504
|
):
|
|
506
505
|
"""Remove subdomains."""
|
|
@@ -509,11 +508,11 @@ def storage_subdomain_remove(
|
|
|
509
508
|
client = create_client(config, profile)
|
|
510
509
|
bucket_id = resolve_bucket_id(client, bucket)
|
|
511
510
|
response = client.delete_bucket_subdomains(bucket_id, subdomains)
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
511
|
+
if response.status_code == 204:
|
|
512
|
+
for subdomain in subdomains:
|
|
513
|
+
print(subdomain)
|
|
514
|
+
else:
|
|
515
|
+
sys.exit(fmt.printer(response))
|
|
517
516
|
|
|
518
517
|
|
|
519
518
|
# ------------------------------------------------------------- #
|
|
@@ -532,7 +531,7 @@ def storage_subdomain_gencert(
|
|
|
532
531
|
client = create_client(config, profile)
|
|
533
532
|
for subdomain in subdomains:
|
|
534
533
|
response = client.gen_cert_for_bucket_subdomain(subdomain)
|
|
535
|
-
if response.status_code ==
|
|
534
|
+
if response.status_code == 204:
|
|
536
535
|
print(subdomain)
|
|
537
536
|
else:
|
|
538
537
|
sys.exit(fmt.printer(response))
|
twc/fmt.py
CHANGED
|
@@ -67,6 +67,9 @@ class Printer:
|
|
|
67
67
|
|
|
68
68
|
def raw(self):
|
|
69
69
|
"""Print raw API response text (mostly raw JSON)."""
|
|
70
|
+
if isinstance(self._data, dict):
|
|
71
|
+
typer.echo(json.dumps(self._data))
|
|
72
|
+
return
|
|
70
73
|
typer.echo(self._data.text)
|
|
71
74
|
|
|
72
75
|
def colorize(self, data: str, lexer: object = JsonLexer()):
|
|
@@ -77,9 +80,12 @@ class Printer:
|
|
|
77
80
|
"""Print colorized JSON output. Fallback to non-color output if
|
|
78
81
|
Pygments not installed and fallback to raw output on JSONDecodeError.
|
|
79
82
|
"""
|
|
83
|
+
data = self._data
|
|
84
|
+
if not isinstance(self._data, dict):
|
|
85
|
+
data = self._data.json()
|
|
80
86
|
try:
|
|
81
87
|
json_data = json.dumps(
|
|
82
|
-
|
|
88
|
+
data, indent=4, sort_keys=True, ensure_ascii=False
|
|
83
89
|
)
|
|
84
90
|
self.colorize(json_data, lexer=JsonLexer())
|
|
85
91
|
except json.JSONDecodeError:
|
|
@@ -89,10 +95,11 @@ class Printer:
|
|
|
89
95
|
"""Print colorized YAML output. Fallback to non-color output if
|
|
90
96
|
Pygments not installed and fallback to raw output on YAMLError.
|
|
91
97
|
"""
|
|
98
|
+
data = self._data
|
|
99
|
+
if not isinstance(self._data, dict):
|
|
100
|
+
data = self._data.json()
|
|
92
101
|
try:
|
|
93
|
-
yaml_data = yaml.dump(
|
|
94
|
-
self._data.json(), sort_keys=True, allow_unicode=True
|
|
95
|
-
)
|
|
102
|
+
yaml_data = yaml.dump(data, sort_keys=True, allow_unicode=True)
|
|
96
103
|
self.colorize(yaml_data, lexer=YamlLexer())
|
|
97
104
|
except yaml.YAMLError:
|
|
98
105
|
self.raw()
|
twc/vars.py
CHANGED
|
@@ -11,4 +11,4 @@ CONTROL_PANEL_URL = "https://timeweb.cloud/my"
|
|
|
11
11
|
REGIONS_WITH_IPV6 = ["ru-1", "pl-1"]
|
|
12
12
|
REGIONS_WITH_IMAGES = ["ru-1", "ru-3", "kz-1", "pl-1", "nl-1"]
|
|
13
13
|
REGIONS_WITH_LAN = ["ru-1", "ru-3", "nl-1", "pl-1"]
|
|
14
|
-
ZONES_WITH_LAN = ["spb-1", "spb-4", "msk-1", "ams-1", "gdn-1"]
|
|
14
|
+
ZONES_WITH_LAN = ["spb-1", "spb-3", "spb-4", "msk-1", "ams-1", "gdn-1"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: twc-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.8.0
|
|
4
4
|
Summary: Timeweb Cloud Command Line Interface.
|
|
5
5
|
Home-page: https://github.com/timeweb-cloud/twc
|
|
6
6
|
License: MIT
|
|
@@ -13,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
17
|
Requires-Dist: colorama (>=0.4.6,<0.5.0)
|
|
17
18
|
Requires-Dist: pygments (>=2.18.0,<3.0.0)
|
|
18
19
|
Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
CHANGELOG.md,sha256=
|
|
1
|
+
CHANGELOG.md,sha256=AnIkFEP7S8yrjLrgp1Vbsp47c79n6RQxz6-6OEwwEmk,26436
|
|
2
2
|
COPYING,sha256=fpJLxZS_kHr_659xkpmqEtqHeZp6lQR9CmKCwnYbsmE,1089
|
|
3
3
|
twc/__init__.py,sha256=NwPAMNw3NuHdWGQvWS9_lromVF6VM194oVOipojfJns,113
|
|
4
|
-
twc/__main__.py,sha256=
|
|
5
|
-
twc/__version__.py,sha256=
|
|
4
|
+
twc/__main__.py,sha256=ADHceaQUzgLmvhYHvb5O8urdJWj5IcEHLpTQkSExiD8,2468
|
|
5
|
+
twc/__version__.py,sha256=OHC5aleI7fbFRKonYAojGfgjUXrryXI8Eh5UyrmuNM4,442
|
|
6
6
|
twc/api/__init__.py,sha256=SXew0Fe51M7nRBNQaaLRH4NjnRHkQUn7J26OCkQsftA,128
|
|
7
7
|
twc/api/base.py,sha256=QRefnIgmlbz8n37GLBKeAK1AtzkcNo1IFjZgHDDECJ4,7912
|
|
8
|
-
twc/api/client.py,sha256=
|
|
8
|
+
twc/api/client.py,sha256=ROuj2ZklQOtgrcXYjoiN2mimwxfNFpy4MZvm0qTfomY,59010
|
|
9
9
|
twc/api/exceptions.py,sha256=UzK3pKRffcXlhnkPy6MDjP_DygVoV17DuZ_mdNbOzts,2369
|
|
10
10
|
twc/api/types.py,sha256=HCxdTi-o8nVq4ShPthd2fUvlYufEoXafx_6qrNHFH04,5406
|
|
11
|
-
twc/apiwrap.py,sha256=
|
|
12
|
-
twc/commands/__init__.py,sha256=
|
|
13
|
-
twc/commands/account.py,sha256=
|
|
11
|
+
twc/apiwrap.py,sha256=hKrg_o6rLfY32SEnWMc1BSXHnSAh7TGar1JQ90YnG5M,2970
|
|
12
|
+
twc/commands/__init__.py,sha256=a-6fHQQwOj--Z7uBZGZL3z1rvJiOGUMQMRET1UknIYo,430
|
|
13
|
+
twc/commands/account.py,sha256=6q9ri02oFbUUZuqNVXO-uHOX45B4ELJlPjyfVaEL5Qw,5960
|
|
14
14
|
twc/commands/balancer.py,sha256=QAouc74ZT5go11gB1vjjfYtd1luTmWrfpACPwokZ5sU,20278
|
|
15
15
|
twc/commands/common.py,sha256=Wph8cVogUNNvc456SQrASb7mv7G88I8ETwHgISVjLQQ,8282
|
|
16
16
|
twc/commands/config.py,sha256=hoRtxn2VRxIsuy9vgO6yd0Cu15Rbl-uYMZeU0Ix7dG0,8797
|
|
17
17
|
twc/commands/database.py,sha256=2NZ-TyRBkFgfYJyUdZUcfdqSaX7QVdWDU4k_yQNtUvo,16052
|
|
18
|
-
twc/commands/domain.py,sha256=
|
|
18
|
+
twc/commands/domain.py,sha256=BIg5k0TDQ-iWnhjuAHaWlZBB0bfaZgqZ2EWZGk3BICA,17154
|
|
19
19
|
twc/commands/firewall.py,sha256=KNolqbi2rsppOZwbs_j3yoZQt-0wKbj1JPGiZdfGxDE,27439
|
|
20
20
|
twc/commands/floating_ip.py,sha256=G9nD5BbHCZcuytbzeneDJWQDhd8c8WRtq9pAfwI9m7E,8747
|
|
21
21
|
twc/commands/image.py,sha256=OviQwegXK55H3TBlroCASVcgj2QUVCTo0ZhF5ug9eT8,8165
|
|
22
22
|
twc/commands/kubernetes.py,sha256=-Cgas1vFVMcrWGinjstuUz3sqX0ZNXv_4mwPwuwKeLE,20870
|
|
23
|
-
twc/commands/project.py,sha256=
|
|
24
|
-
twc/commands/server.py,sha256=
|
|
23
|
+
twc/commands/project.py,sha256=xnL3kLIumKzrI9EZ6r6m-PGOl3mZ9IhLQua7WZ3Rghg,10499
|
|
24
|
+
twc/commands/server.py,sha256=Cw8VxOcWEVPtcKZ53h9erhV3VOj7io9E8xQwVN0S53Y,70294
|
|
25
25
|
twc/commands/ssh_key.py,sha256=NHgTPhAQpDzt-iPHHVo4XqUJvujNqf019N6N9qYZ9Us,7941
|
|
26
|
-
twc/commands/storage.py,sha256=
|
|
26
|
+
twc/commands/storage.py,sha256=Pztk5iUBp9RtkdOwsfHaZFCnD8GuH6zOPtluawkRmiI,19404
|
|
27
27
|
twc/commands/vpc.py,sha256=SAht6UD17mU0d_AZY6W34VEYs7CqUsS2iDakPFxAFQU,8876
|
|
28
|
-
twc/fmt.py,sha256=
|
|
28
|
+
twc/fmt.py,sha256=nbuYZ8nVabYDwCmZqnL3-c6Tmri4B-R_sTCkG6sdfeI,7171
|
|
29
29
|
twc/typerx.py,sha256=AZ6BgTQvlrZYfKVYd9YqRNQnAR2XuyqImz4rf6di6f4,6737
|
|
30
30
|
twc/utils.py,sha256=uWizyUC4dHLwtk50q4Sub3zOvnVESfHKBbXYwk5t71w,651
|
|
31
|
-
twc/vars.py,sha256=
|
|
32
|
-
twc_cli-2.
|
|
33
|
-
twc_cli-2.
|
|
34
|
-
twc_cli-2.
|
|
35
|
-
twc_cli-2.
|
|
36
|
-
twc_cli-2.
|
|
31
|
+
twc/vars.py,sha256=fva3O2leMGtExb1aWiAk6sOV0O8et9_kEyRpYYIZ7CM,543
|
|
32
|
+
twc_cli-2.8.0.dist-info/COPYING,sha256=fpJLxZS_kHr_659xkpmqEtqHeZp6lQR9CmKCwnYbsmE,1089
|
|
33
|
+
twc_cli-2.8.0.dist-info/METADATA,sha256=LEAHMaB0puxa1JSU4C1ok6O1S1p99CObd5VpDuB1jEY,2652
|
|
34
|
+
twc_cli-2.8.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
35
|
+
twc_cli-2.8.0.dist-info/entry_points.txt,sha256=tmTaVRhm8BkNrXC_9XJMum7O9wFVOvkXcBetxmahWvE,40
|
|
36
|
+
twc_cli-2.8.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|