twc-cli 2.10.1__py3-none-any.whl → 2.12.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 CHANGED
@@ -2,6 +2,36 @@
2
2
 
3
3
  В этом файле описаны все значимые изменения в Timeweb Cloud CLI. В выпусках мы придерживается правил [семантического версионирования](https://semver.org/lang/ru/).
4
4
 
5
+ # Версия 2.12.0 (2025.xx.xx)
6
+
7
+ ## Добавлено
8
+
9
+ - Добавлены опции `--max-connections`, `--connect-timeout`, `--client-timeout`, `--server-timeout` в команды `twc balancer create`, `twc balancer set`.
10
+ - В регионе `kz-1` для облачных серверов теперь доступен IPv6.
11
+
12
+ ## Изменено
13
+
14
+ - Стандартный ендпоинт объектного хранилища изменён с `s3.timeweb.cloud` на `s3.twcstorage.ru`.
15
+
16
+ # Версия 2.11.0 (2025.04.01)
17
+
18
+ ## Добавлено
19
+
20
+ - Добавлены новые опции к команде `twc balancer create`:
21
+ - для настройки сети: `--network-id`, `--public-ip`, `--no-public-ip`, `--private-ip`;
22
+ - для установки TLS: `--cert-type`, `--cert-domain`, `--cert-data`, `--cert-key`;
23
+ - выбор локации и пресета: `--region`, `--availability-zone`, `--preset-id`;
24
+ - дополнительные: `--desc` (описание балансировщика нагрузки).
25
+ - Добавлена новая команда для просмотра списка доступных пресетов балансировщиков нагрузки — `twc balancer list-presets`.
26
+
27
+ ## Изменено
28
+
29
+ - Опция `--network` команды `twc balancer create` объявлена устаревшей и скрыта. Вместо неё используйте `--network-id`.
30
+
31
+ ## Исправлено
32
+
33
+ - Исправлена ошибка в команде `twc server create` возникавшая при передаче парамера `--project-id`.
34
+
5
35
  # Версия 2.10.1 (2025.03.25)
6
36
 
7
37
  ## Исправлено
twc/__version__.py CHANGED
@@ -12,5 +12,5 @@
12
12
  import sys
13
13
 
14
14
 
15
- __version__ = "2.10.1"
15
+ __version__ = "2.12.0"
16
16
  __pyversion__ = sys.version.replace("\n", "")
twc/api/client.py CHANGED
@@ -78,6 +78,7 @@ class TimewebCloud(TimewebCloudBase):
78
78
  network: Optional[dict] = None,
79
79
  availability_zone: Optional[ServiceAvailabilityZone] = None,
80
80
  is_root_password_required: Optional[bool] = None,
81
+ project_id: Optional[int] = None,
81
82
  ):
82
83
  """Create new Cloud Server. Note:
83
84
 
@@ -120,6 +121,7 @@ class TimewebCloud(TimewebCloudBase):
120
121
  if is_root_password_required is not None
121
122
  else {}
122
123
  ),
124
+ **({"project_id": project_id} if project_id else {}),
123
125
  }
124
126
 
125
127
  return self._request("POST", f"{self.api_url}/servers", json=payload)
@@ -1157,6 +1159,14 @@ class TimewebCloud(TimewebCloudBase):
1157
1159
  force_https: bool = False,
1158
1160
  backend_keepalive: bool = False,
1159
1161
  network: Optional[dict] = None,
1162
+ comment: Optional[str] = None,
1163
+ project_id: Optional[int] = None,
1164
+ certificates: Optional[dict] = None,
1165
+ max_connections: Optional[int] = None,
1166
+ connect_timeout: Optional[int] = None,
1167
+ client_timeout: Optional[int] = None,
1168
+ server_timeout: Optional[int] = None,
1169
+ http_timeout: Optional[int] = None,
1160
1170
  ):
1161
1171
  """Create load balancer."""
1162
1172
  payload = {
@@ -1175,6 +1185,34 @@ class TimewebCloud(TimewebCloudBase):
1175
1185
  "is_ssl": force_https,
1176
1186
  "is_keepalive": backend_keepalive,
1177
1187
  **({"network": network} if network else {}),
1188
+ **({"comment": comment} if comment else {}),
1189
+ **({"project_id": project_id} if project_id else {}),
1190
+ **({"certificates": certificates} if certificates else {}),
1191
+ **(
1192
+ {"maxconn": max_connections}
1193
+ if max_connections is not None
1194
+ else {}
1195
+ ),
1196
+ **(
1197
+ {"connect_timeout": connect_timeout}
1198
+ if connect_timeout is not None
1199
+ else {}
1200
+ ),
1201
+ **(
1202
+ {"client_timeout": client_timeout}
1203
+ if client_timeout is not None
1204
+ else {}
1205
+ ),
1206
+ **(
1207
+ {"server_timeout": server_timeout}
1208
+ if server_timeout is not None
1209
+ else {}
1210
+ ),
1211
+ **(
1212
+ {"httprequest_timeout": http_timeout}
1213
+ if http_timeout is not None
1214
+ else {}
1215
+ ),
1178
1216
  }
1179
1217
  return self._request("POST", f"{self.api_url}/balancers", json=payload)
1180
1218
 
@@ -1195,6 +1233,11 @@ class TimewebCloud(TimewebCloudBase):
1195
1233
  proxy_protocol: Optional[bool] = None,
1196
1234
  force_https: Optional[bool] = None,
1197
1235
  backend_keepalive: Optional[bool] = None,
1236
+ max_connections: Optional[int] = None,
1237
+ connect_timeout: Optional[int] = None,
1238
+ client_timeout: Optional[int] = None,
1239
+ server_timeout: Optional[int] = None,
1240
+ http_timeout: Optional[int] = None,
1198
1241
  ):
1199
1242
  """Update load balancer settings."""
1200
1243
  payload = {
@@ -1220,6 +1263,31 @@ class TimewebCloud(TimewebCloudBase):
1220
1263
  if backend_keepalive is not None
1221
1264
  else {}
1222
1265
  ),
1266
+ **(
1267
+ {"maxconn": max_connections}
1268
+ if max_connections is not None
1269
+ else {}
1270
+ ),
1271
+ **(
1272
+ {"connect_timeout": connect_timeout}
1273
+ if connect_timeout is not None
1274
+ else {}
1275
+ ),
1276
+ **(
1277
+ {"client_timeout": client_timeout}
1278
+ if client_timeout is not None
1279
+ else {}
1280
+ ),
1281
+ **(
1282
+ {"server_timeout": server_timeout}
1283
+ if server_timeout is not None
1284
+ else {}
1285
+ ),
1286
+ **(
1287
+ {"httprequest_timeout": http_timeout}
1288
+ if http_timeout is not None
1289
+ else {}
1290
+ ),
1223
1291
  }
1224
1292
  return self._request(
1225
1293
  "PATCH", f"{self.api_url}/balancers/{balancer_id}", json=payload
twc/api/exceptions.py CHANGED
@@ -15,11 +15,13 @@ class ErrResponse:
15
15
  error_code: str = None,
16
16
  message: Union[str, List[str]] = None,
17
17
  response_id: UUID = None,
18
+ **kwargs,
18
19
  ):
19
20
  self.status_code = status_code
20
21
  self.error_code = error_code
21
22
  self.message = message
22
23
  self.response_id = response_id
24
+ self.kwargs = kwargs
23
25
 
24
26
 
25
27
  class TimewebCloudException(HTTPError):
twc/commands/balancer.py CHANGED
@@ -2,9 +2,11 @@
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
  from logging import debug
9
+ from ipaddress import IPv4Address, IPv4Network
8
10
 
9
11
  import typer
10
12
  from requests import Response
@@ -13,20 +15,23 @@ from twc import fmt
13
15
  from twc.typerx import TyperAlias
14
16
  from twc.apiwrap import create_client
15
17
  from twc.api import TimewebCloud
16
- from twc.api.types import LoadBalancerAlgo, LoadBalancerProto
18
+ from twc.vars import REGION_ZONE_MAP
19
+ from twc.api.types import LoadBalancerAlgo, LoadBalancerProto, ServiceRegion
17
20
  from .common import (
18
21
  verbose_option,
19
22
  config_option,
20
23
  profile_option,
21
24
  filter_option,
22
25
  yes_option,
26
+ region_option,
27
+ zone_option,
23
28
  output_format_option,
24
29
  load_from_config_callback,
25
30
  )
26
31
 
27
32
 
28
33
  balancer = TyperAlias(help=__doc__)
29
- balancer_backend = TyperAlias(help="Manage load balancer backends.")
34
+ balancer_backend = TyperAlias(help="Manage load balancer backend servers.")
30
35
  balancer_rule = TyperAlias(help="Manage load balancer rules.")
31
36
  balancer.add_typer(balancer_backend, name="backend", aliases=["backends"])
32
37
  balancer.add_typer(balancer_rule, name="rule", aliases=["rules"])
@@ -144,6 +149,13 @@ def balancer_get(
144
149
  # ------------------------------------------------------------- #
145
150
 
146
151
 
152
+ class CertType(str, Enum):
153
+ """..."""
154
+
155
+ CUSTOM = "custom"
156
+ LETS_ENCRYPT = "lets_encrypt"
157
+
158
+
147
159
  @balancer.command("create")
148
160
  def balancer_create(
149
161
  verbose: Optional[bool] = verbose_option,
@@ -151,11 +163,17 @@ def balancer_create(
151
163
  profile: Optional[str] = profile_option,
152
164
  output_format: Optional[str] = output_format_option,
153
165
  name: str = typer.Option(..., help="Load balancer display name."),
166
+ desc: Optional[str] = typer.Option(
167
+ None, help="Load balancer description."
168
+ ),
169
+ preset_id: Optional[int] = typer.Option(
170
+ None, help="Load balancer preset ID."
171
+ ),
154
172
  replicas: int = typer.Option(
155
173
  1,
156
174
  min=1,
157
175
  max=2,
158
- help="Load balancer replica count.",
176
+ help="Load balancer replica count. Ignored if --preset-id set.",
159
177
  ),
160
178
  algo: LoadBalancerAlgo = typer.Option(
161
179
  LoadBalancerAlgo.ROUND_ROBIN.value,
@@ -180,6 +198,22 @@ def balancer_create(
180
198
  proxy_protocol: bool = typer.Option(False),
181
199
  force_https: bool = typer.Option(False),
182
200
  backend_keepalive: bool = typer.Option(False),
201
+ max_connections: Optional[int] = typer.Option(
202
+ None, help="Backend server's maximum number of concurrent connections."
203
+ ),
204
+ connect_timeout: Optional[int] = typer.Option(
205
+ None,
206
+ help="Maximum time to wait for a connection attempt to a backend server to succeed.",
207
+ ),
208
+ client_timeout: Optional[int] = typer.Option(
209
+ None, help="Maximum inactivity time on the client side."
210
+ ),
211
+ server_timeout: Optional[int] = typer.Option(
212
+ None, help="Maximum time for pending data staying into output buffer."
213
+ ),
214
+ http_timeout: Optional[int] = typer.Option(
215
+ None, help="Maximum allowed time to wait for a complete HTTP request."
216
+ ),
183
217
  project_id: Optional[int] = typer.Option(
184
218
  None,
185
219
  envvar="TWC_PROJECT",
@@ -187,28 +221,44 @@ def balancer_create(
187
221
  callback=load_from_config_callback,
188
222
  help="Add load balancer to specific project.",
189
223
  ),
190
- network: Optional[str] = typer.Option(None, help="Private network ID."),
224
+ network: Optional[str] = typer.Option(
225
+ None, hidden=True, help="Private network ID."
226
+ ),
227
+ network_id: Optional[str] = typer.Option(None, help="Private network ID."),
228
+ private_ip: Optional[str] = typer.Option(
229
+ None, help="Private IPv4 address."
230
+ ),
231
+ public_ip: Optional[str] = typer.Option(
232
+ None, help="Public IPv4 address. New address by default."
233
+ ),
234
+ no_public_ip: Optional[bool] = typer.Option(
235
+ False, "--no-public-ip", help="Do not add public IPv4 address."
236
+ ),
237
+ region: Optional[str] = region_option,
238
+ availability_zone: Optional[str] = zone_option,
239
+ cert_type: Optional[CertType] = typer.Option(
240
+ None,
241
+ help="SSL certificate type. Falls to 'custom' "
242
+ "if --cert-data and --cert-key set.",
243
+ ),
244
+ cert_domain: Optional[str] = typer.Option(
245
+ None,
246
+ help="Domain name for which the certificate was issued. "
247
+ "Note: domain name A-record will set to load balancer's public IP.",
248
+ ),
249
+ cert_data: Optional[typer.FileText] = typer.Option(
250
+ None, help="Fullchain certificate file."
251
+ ),
252
+ cert_key: Optional[typer.FileText] = typer.Option(
253
+ None, help="Certificate key file."
254
+ ),
191
255
  ):
192
256
  """Create load balancer."""
193
257
  client = create_client(config, profile)
194
258
 
195
- preset_id = None
196
- for preset in client.get_load_balancer_presets().json()[
197
- "balancers_presets"
198
- ]:
199
- if preset["replica_count"] == replicas:
200
- preset_id = preset["id"]
201
- if not preset_id:
202
- sys.exit(f"Error: Cannot set {replicas} load balancer replicas.")
203
-
204
- if project_id:
205
- if not project_id in [
206
- prj["id"] for prj in client.get_projects().json()["projects"]
207
- ]:
208
- sys.exit(f"Wrong project ID: Project '{project_id}' not found.")
209
-
210
259
  payload = {
211
260
  "name": name,
261
+ "comment": desc,
212
262
  "preset_id": preset_id,
213
263
  "algo": algo,
214
264
  "proto": proto,
@@ -222,21 +272,113 @@ def balancer_create(
222
272
  "proxy_protocol": proxy_protocol,
223
273
  "force_https": force_https,
224
274
  "backend_keepalive": backend_keepalive,
275
+ "network": {},
276
+ "project_id": project_id,
277
+ "certificates": {},
278
+ "max_connections": max_connections,
279
+ "connect_timeout": connect_timeout,
280
+ "client_timeout": client_timeout,
281
+ "server_timeout": server_timeout,
282
+ "http_timeout": http_timeout,
225
283
  }
226
284
 
285
+ if cert_type == CertType.CUSTOM:
286
+ if not cert_data or not cert_key:
287
+ sys.exit(
288
+ "Error: --cert-data and --cert-key is required if --cert-type is 'custom'"
289
+ )
290
+ elif cert_type == CertType.LETS_ENCRYPT:
291
+ if cert_data or cert_key:
292
+ sys.exit(
293
+ "Error: --cert-data and --cert-key is not allowed with --cert-type 'lets_encrypt'"
294
+ )
295
+ if not cert_type:
296
+ if cert_data and not cert_key:
297
+ sys.exit("Error: --cert-key is required.")
298
+ if cert_key and not cert_data:
299
+ sys.exit("Error: --cert-data is required.")
300
+ if cert_data and cert_key:
301
+ cert_type = CertType.CUSTOM
302
+ if cert_type and not cert_domain:
303
+ sys.exit(
304
+ "Error: --cert-domain is required if --cert-type and/or --cert-data, --cert-key is set."
305
+ )
306
+ if cert_type:
307
+ payload["certificates"] = {
308
+ "type": cert_type.value,
309
+ "fqdn": cert_domain,
310
+ **({"cert_data": cert_data.read()} if cert_data else {}),
311
+ **({"key_data": cert_key.read()} if cert_key else {}),
312
+ }
313
+
314
+ if not preset_id:
315
+ for preset in client.get_load_balancer_presets().json()[
316
+ "balancers_presets"
317
+ ]:
318
+ if (
319
+ preset["replica_count"] == replicas
320
+ and preset["location"] == region
321
+ ):
322
+ preset_id = preset["id"]
323
+ if not preset_id:
324
+ sys.exit(f"Error: Cannot set {replicas} load balancer replicas.")
325
+
227
326
  if network:
228
- payload["network"] = {"id": network}
327
+ print(
328
+ "Option --network is deprecated and will be removed soon, "
329
+ "use --network-id instead",
330
+ file=sys.stderr,
331
+ )
332
+ network_id = network
333
+
334
+ if network_id:
335
+ payload["network"]["id"] = network_id
336
+ if private_ip:
337
+ net = IPv4Network(
338
+ client.get_vpc(network_id).json()["vpc"]["subnet_v4"]
339
+ )
340
+ if IPv4Address(private_ip) >= net.network_address + 4:
341
+ payload["network"]["ip"] = private_ip
342
+ else:
343
+ # First 3 addresses is reserved by Timeweb Cloud for gateway and future use.
344
+ sys.exit(
345
+ f"Error: Private address '{private_ip}' is not allowed. "
346
+ "IP must be at least the fourth in order in the network."
347
+ )
348
+ if public_ip:
349
+ try:
350
+ _ = IPv4Address(public_ip)
351
+ payload["network"]["floating_ip"] = public_ip
352
+ except ValueError:
353
+ sys.exit(f"Error: '{public_ip}' is not valid IPv4 address.")
354
+ else:
355
+ # Get new public IPv4 address.
356
+ if no_public_ip is False:
357
+ zone = None
358
+ if not preset_id and not availability_zone and not region:
359
+ sys.exit(
360
+ "Error: Unable to get IPv4 address, at least one of "
361
+ "[--preset-id, --region, --availability-zone] "
362
+ "must be set to determine correct location."
363
+ )
364
+ if region:
365
+ zone = REGION_ZONE_MAP[region]
366
+ if preset_id and not availability_zone:
367
+ for preset in client.get_database_presets().json()[
368
+ "databases_presets"
369
+ ]:
370
+ if preset["id"] == preset_id:
371
+ zone = REGION_ZONE_MAP[preset["location"]]
372
+ if availability_zone:
373
+ zone = availability_zone
374
+ ip = client.create_floating_ip(availability_zone=zone).json()[
375
+ "ip"
376
+ ]["ip"]
377
+ payload["network"]["floating_ip"] = ip
229
378
 
230
379
  # Create LB
231
380
  response = client.create_load_balancer(**payload)
232
381
 
233
- # Add created LB to project if set
234
- if project_id:
235
- client.add_balancer_to_project(
236
- response.json()["balancer"]["id"],
237
- project_id,
238
- )
239
-
240
382
  fmt.printer(
241
383
  response,
242
384
  output_format=output_format,
@@ -291,6 +433,22 @@ def balancer_set(
291
433
  proxy_protocol: Optional[bool] = typer.Option(None),
292
434
  force_https: Optional[bool] = typer.Option(None),
293
435
  backend_keepalive: Optional[bool] = typer.Option(None),
436
+ max_connections: Optional[int] = typer.Option(
437
+ None, help="Backend server's maximum number of concurrent connections."
438
+ ),
439
+ connect_timeout: Optional[int] = typer.Option(
440
+ None,
441
+ help="Maximum time to wait for a connection attempt to a backend server to succeed.",
442
+ ),
443
+ client_timeout: Optional[int] = typer.Option(
444
+ None, help="Maximum inactivity time on the client side."
445
+ ),
446
+ server_timeout: Optional[int] = typer.Option(
447
+ None, help="Maximum time for pending data staying into output buffer."
448
+ ),
449
+ http_timeout: Optional[int] = typer.Option(
450
+ None, help="Maximum allowed time to wait for a complete HTTP request."
451
+ ),
294
452
  ):
295
453
  """Change load balancer parameters."""
296
454
  client = create_client(config, profile)
@@ -321,6 +479,11 @@ def balancer_set(
321
479
  proxy_protocol=proxy_protocol,
322
480
  force_https=force_https,
323
481
  backend_keepalive=backend_keepalive,
482
+ max_connections=max_connections,
483
+ connect_timeout=connect_timeout,
484
+ client_timeout=client_timeout,
485
+ server_timeout=server_timeout,
486
+ http_timeout=http_timeout,
324
487
  )
325
488
 
326
489
  fmt.printer(
@@ -384,6 +547,64 @@ def blancer_backend_list(
384
547
  )
385
548
 
386
549
 
550
+ # ------------------------------------------------------------- #
551
+ # $ twc balancer list-presets #
552
+ # ------------------------------------------------------------- #
553
+
554
+
555
+ def _print_presets(response: Response, filters: Optional[str] = None):
556
+ presets = response.json()["balancers_presets"]
557
+ if filters:
558
+ presets = fmt.filter_list(presets, filters)
559
+ table = fmt.Table()
560
+ table.header(
561
+ [
562
+ "ID",
563
+ "REGION",
564
+ "PRICE",
565
+ "REPLICAS",
566
+ "BANDW",
567
+ "RPS",
568
+ ]
569
+ )
570
+ for preset in presets:
571
+ table.row(
572
+ [
573
+ preset["id"],
574
+ preset["location"],
575
+ preset["price"],
576
+ preset["replica_count"],
577
+ preset["bandwidth"],
578
+ preset["request_per_second"],
579
+ ]
580
+ )
581
+ table.print()
582
+
583
+
584
+ @balancer.command("list-presets", "lp")
585
+ def balancer_list_presets(
586
+ verbose: Optional[bool] = verbose_option,
587
+ config: Optional[Path] = config_option,
588
+ profile: Optional[str] = profile_option,
589
+ output_format: Optional[str] = output_format_option,
590
+ filters: Optional[str] = filter_option,
591
+ region: Optional[ServiceRegion] = typer.Option(
592
+ None, help="Use region (location)."
593
+ ),
594
+ ):
595
+ """List configuration presets."""
596
+ if region:
597
+ filters = f"{filters},location:{region}"
598
+ client = create_client(config, profile)
599
+ response = client.get_load_balancer_presets()
600
+ fmt.printer(
601
+ response,
602
+ output_format=output_format,
603
+ filters=filters,
604
+ func=_print_presets,
605
+ )
606
+
607
+
387
608
  # ------------------------------------------------------------- #
388
609
  # $ twc balancer backend add #
389
610
  # ------------------------------------------------------------- #
@@ -392,7 +613,7 @@ def blancer_backend_list(
392
613
  @balancer_backend.command("add")
393
614
  def blancer_backend_add(
394
615
  balancer_id: int = typer.Argument(...),
395
- backends: List[str] = typer.Argument(..., metavar="BACKEND..."),
616
+ backends: List[str] = typer.Argument(..., metavar="BACKEND_IP..."),
396
617
  verbose: Optional[bool] = verbose_option,
397
618
  config: Optional[Path] = config_option,
398
619
  profile: Optional[str] = profile_option,
twc/commands/storage.py CHANGED
@@ -611,7 +611,7 @@ def storage_genconfig(
611
611
  "rclone": RCLONE_CONFIG_TEMPLATE.strip(),
612
612
  }
613
613
 
614
- endpoint = "s3.timeweb.cloud"
614
+ endpoint = "s3.twcstorage.ru"
615
615
  if not access_key.isupper():
616
616
  # Legacy object storage service have lowercase usernames only.
617
617
  # New storage, on the contrary, always has keys in uppercase.
twc/vars.py CHANGED
@@ -8,7 +8,7 @@ expand or other infrastructure or product changes occur.
8
8
  CONTROL_PANEL_URL = "https://timeweb.cloud/my"
9
9
 
10
10
  # Location specific parameters. May change later.
11
- REGIONS_WITH_IPV6 = ["ru-1", "ru-3", "pl-1", "nl-1"]
11
+ REGIONS_WITH_IPV6 = ["ru-1", "ru-3", "pl-1", "nl-1", "kz-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", "de-1"]
14
14
  ZONES_WITH_LAN = [
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: twc-cli
3
- Version: 2.10.1
3
+ Version: 2.12.0
4
4
  Summary: Timeweb Cloud Command Line Interface.
5
5
  Home-page: https://github.com/timeweb-cloud/twc
6
6
  License: MIT
@@ -1,17 +1,17 @@
1
- CHANGELOG.md,sha256=Qs4nN5qu8rQUqUgHQoGBQThGm0k5BaTLOx6BE6NiXWY,29824
1
+ CHANGELOG.md,sha256=L4iyG04B4hlQYlj6OXJQWvOIS0f-pmCZVbvV7xlsap8,31472
2
2
  COPYING,sha256=fpJLxZS_kHr_659xkpmqEtqHeZp6lQR9CmKCwnYbsmE,1089
3
3
  twc/__init__.py,sha256=NwPAMNw3NuHdWGQvWS9_lromVF6VM194oVOipojfJns,113
4
4
  twc/__main__.py,sha256=ADHceaQUzgLmvhYHvb5O8urdJWj5IcEHLpTQkSExiD8,2468
5
- twc/__version__.py,sha256=8icMDOUZhpUZ9qC5kE9GWifcKzo2RYQElI1jYfMVJCs,443
5
+ twc/__version__.py,sha256=vyBciXWNGbjPeCy9tHWEKQFwm1VPc6m1_8-OvqI6EeA,443
6
6
  twc/api/__init__.py,sha256=SXew0Fe51M7nRBNQaaLRH4NjnRHkQUn7J26OCkQsftA,128
7
7
  twc/api/base.py,sha256=QRefnIgmlbz8n37GLBKeAK1AtzkcNo1IFjZgHDDECJ4,7912
8
- twc/api/client.py,sha256=T74KLeKHyAxzKHkpdNF-VOkfiAwnu7us61xzPosV5_o,64366
9
- twc/api/exceptions.py,sha256=UzK3pKRffcXlhnkPy6MDjP_DygVoV17DuZ_mdNbOzts,2369
8
+ twc/api/client.py,sha256=ovP8Dz1A8SjTisIL0Vr2scCRYN_SJg9fayA1f6SQix8,66773
9
+ twc/api/exceptions.py,sha256=6nMU20f-Xe7EbH2jpfmSGMW69Rwfhh_ph9ygz37G0XY,2416
10
10
  twc/api/types.py,sha256=uagnD3TPpoJFYUFK6HfHQPlEXs2GLxuJdjhNIbraXwM,5374
11
11
  twc/apiwrap.py,sha256=hKrg_o6rLfY32SEnWMc1BSXHnSAh7TGar1JQ90YnG5M,2970
12
12
  twc/commands/__init__.py,sha256=a-6fHQQwOj--Z7uBZGZL3z1rvJiOGUMQMRET1UknIYo,430
13
13
  twc/commands/account.py,sha256=6q9ri02oFbUUZuqNVXO-uHOX45B4ELJlPjyfVaEL5Qw,5960
14
- twc/commands/balancer.py,sha256=QAouc74ZT5go11gB1vjjfYtd1luTmWrfpACPwokZ5sU,20278
14
+ twc/commands/balancer.py,sha256=SygXDpJpjybsfxfCN02-WsWyjLS-77qEGuYZoxsfmA4,28382
15
15
  twc/commands/common.py,sha256=Wph8cVogUNNvc456SQrASb7mv7G88I8ETwHgISVjLQQ,8282
16
16
  twc/commands/config.py,sha256=xHNEZVmM60c9dApLfNsj78sXZk6VsFwPdVIHO9r8xks,8802
17
17
  twc/commands/database.py,sha256=NOi5b-DGYgbbN7bPrsJt0wKTJBzrfKUPwltVx8YGsgU,31953
@@ -23,14 +23,14 @@ twc/commands/kubernetes.py,sha256=-Cgas1vFVMcrWGinjstuUz3sqX0ZNXv_4mwPwuwKeLE,20
23
23
  twc/commands/project.py,sha256=xnL3kLIumKzrI9EZ6r6m-PGOl3mZ9IhLQua7WZ3Rghg,10499
24
24
  twc/commands/server.py,sha256=5yb_pdB5BOoj_UAWdMxiCtuGdRBgcllkStMqyRSlx9k,72315
25
25
  twc/commands/ssh_key.py,sha256=NHgTPhAQpDzt-iPHHVo4XqUJvujNqf019N6N9qYZ9Us,7941
26
- twc/commands/storage.py,sha256=Pztk5iUBp9RtkdOwsfHaZFCnD8GuH6zOPtluawkRmiI,19404
26
+ twc/commands/storage.py,sha256=u7R6L3vZr1YdAVUw-oENAbrkdci2dMb-7E1RkPK9KLg,19404
27
27
  twc/commands/vpc.py,sha256=SAht6UD17mU0d_AZY6W34VEYs7CqUsS2iDakPFxAFQU,8876
28
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=AHZEwtQ_BQbnP0n7RXJ-qhqeKPQ_FPc8SDgg6osR5uU,822
32
- twc_cli-2.10.1.dist-info/COPYING,sha256=fpJLxZS_kHr_659xkpmqEtqHeZp6lQR9CmKCwnYbsmE,1089
33
- twc_cli-2.10.1.dist-info/METADATA,sha256=-BCPh3qtm2gn6edv2ih7nODdb0M0n8k8LMz2sGc5Dbs,2653
34
- twc_cli-2.10.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
35
- twc_cli-2.10.1.dist-info/entry_points.txt,sha256=tmTaVRhm8BkNrXC_9XJMum7O9wFVOvkXcBetxmahWvE,40
36
- twc_cli-2.10.1.dist-info/RECORD,,
31
+ twc/vars.py,sha256=ljW52q6XwsbQqOBPGhs5rbZ5ZqVvQXdKqUt8mLhXRts,830
32
+ twc_cli-2.12.0.dist-info/COPYING,sha256=fpJLxZS_kHr_659xkpmqEtqHeZp6lQR9CmKCwnYbsmE,1089
33
+ twc_cli-2.12.0.dist-info/METADATA,sha256=q-YUNB0fPiGn1yZi7SzoKdMeuIp2lr2yQR0r4WBd2Io,2653
34
+ twc_cli-2.12.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
35
+ twc_cli-2.12.0.dist-info/entry_points.txt,sha256=tmTaVRhm8BkNrXC_9XJMum7O9wFVOvkXcBetxmahWvE,40
36
+ twc_cli-2.12.0.dist-info/RECORD,,