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 +30 -0
- twc/__version__.py +1 -1
- twc/api/client.py +68 -0
- twc/api/exceptions.py +2 -0
- twc/commands/balancer.py +249 -28
- twc/commands/storage.py +1 -1
- twc/vars.py +1 -1
- {twc_cli-2.10.1.dist-info → twc_cli-2.12.0.dist-info}/METADATA +1 -1
- {twc_cli-2.10.1.dist-info → twc_cli-2.12.0.dist-info}/RECORD +12 -12
- {twc_cli-2.10.1.dist-info → twc_cli-2.12.0.dist-info}/COPYING +0 -0
- {twc_cli-2.10.1.dist-info → twc_cli-2.12.0.dist-info}/WHEEL +0 -0
- {twc_cli-2.10.1.dist-info → twc_cli-2.12.0.dist-info}/entry_points.txt +0 -0
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
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.
|
|
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
|
|
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(
|
|
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
|
-
|
|
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="
|
|
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.
|
|
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,17 +1,17 @@
|
|
|
1
|
-
CHANGELOG.md,sha256=
|
|
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=
|
|
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=
|
|
9
|
-
twc/api/exceptions.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|