twc-cli 2.10.1__tar.gz → 2.11.0__tar.gz
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.
- {twc_cli-2.10.1 → twc_cli-2.11.0}/CHANGELOG.md +19 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/PKG-INFO +1 -1
- {twc_cli-2.10.1 → twc_cli-2.11.0}/pyproject.toml +1 -1
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/__version__.py +1 -1
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/api/client.py +8 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/api/exceptions.py +2 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/commands/balancer.py +207 -28
- {twc_cli-2.10.1 → twc_cli-2.11.0}/COPYING +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/README.md +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/__init__.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/__main__.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/api/__init__.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/api/base.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/api/types.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/apiwrap.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/commands/__init__.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/commands/account.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/commands/common.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/commands/config.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/commands/database.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/commands/domain.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/commands/firewall.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/commands/floating_ip.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/commands/image.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/commands/kubernetes.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/commands/project.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/commands/server.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/commands/ssh_key.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/commands/storage.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/commands/vpc.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/fmt.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/typerx.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/utils.py +0 -0
- {twc_cli-2.10.1 → twc_cli-2.11.0}/twc/vars.py +0 -0
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
В этом файле описаны все значимые изменения в Timeweb Cloud CLI. В выпусках мы придерживается правил [семантического версионирования](https://semver.org/lang/ru/).
|
|
4
4
|
|
|
5
|
+
# Версия 2.11.0 (2025.04.01)
|
|
6
|
+
|
|
7
|
+
## Добавлено
|
|
8
|
+
|
|
9
|
+
- Добавлены новые опции к команде `twc balancer create`:
|
|
10
|
+
- для настройки сети: `--network-id`, `--public-ip`, `--no-public-ip`, `--private-ip`;
|
|
11
|
+
- для установки TLS: `--cert-type`, `--cert-domain`, `--cert-data`, `--cert-key`;
|
|
12
|
+
- выбор локации и пресета: `--region`, `--availability-zone`, `--preset-id`;
|
|
13
|
+
- дополнительные: `--desc` (описание балансировщика нагрузки).
|
|
14
|
+
- Добавлена новая команда для просмотра списка доступных пресетов балансировщиков нагрузки — `twc balancer list-presets`.
|
|
15
|
+
|
|
16
|
+
## Изменено
|
|
17
|
+
|
|
18
|
+
- Опция `--network` команды `twc balancer create` объявлена устаревшей и скрыта. Вместо неё используйте `--network-id`.
|
|
19
|
+
|
|
20
|
+
## Исправлено
|
|
21
|
+
|
|
22
|
+
- Исправлена ошибка в команде `twc server create` возникавшая при передаче парамера `--project-id`.
|
|
23
|
+
|
|
5
24
|
# Версия 2.10.1 (2025.03.25)
|
|
6
25
|
|
|
7
26
|
## Исправлено
|
|
@@ -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,9 @@ 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,
|
|
1160
1165
|
):
|
|
1161
1166
|
"""Create load balancer."""
|
|
1162
1167
|
payload = {
|
|
@@ -1175,6 +1180,9 @@ class TimewebCloud(TimewebCloudBase):
|
|
|
1175
1180
|
"is_ssl": force_https,
|
|
1176
1181
|
"is_keepalive": backend_keepalive,
|
|
1177
1182
|
**({"network": network} if network else {}),
|
|
1183
|
+
**({"comment": comment} if comment else {}),
|
|
1184
|
+
**({"project_id": project_id} if project_id else {}),
|
|
1185
|
+
**({"certificates": certificates} if certificates else {}),
|
|
1178
1186
|
}
|
|
1179
1187
|
return self._request("POST", f"{self.api_url}/balancers", json=payload)
|
|
1180
1188
|
|
|
@@ -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):
|
|
@@ -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,
|
|
@@ -187,28 +205,44 @@ def balancer_create(
|
|
|
187
205
|
callback=load_from_config_callback,
|
|
188
206
|
help="Add load balancer to specific project.",
|
|
189
207
|
),
|
|
190
|
-
network: Optional[str] = typer.Option(
|
|
208
|
+
network: Optional[str] = typer.Option(
|
|
209
|
+
None, hidden=True, help="Private network ID."
|
|
210
|
+
),
|
|
211
|
+
network_id: Optional[str] = typer.Option(None, help="Private network ID."),
|
|
212
|
+
private_ip: Optional[str] = typer.Option(
|
|
213
|
+
None, help="Private IPv4 address."
|
|
214
|
+
),
|
|
215
|
+
public_ip: Optional[str] = typer.Option(
|
|
216
|
+
None, help="Public IPv4 address. New address by default."
|
|
217
|
+
),
|
|
218
|
+
no_public_ip: Optional[bool] = typer.Option(
|
|
219
|
+
False, "--no-public-ip", help="Do not add public IPv4 address."
|
|
220
|
+
),
|
|
221
|
+
region: Optional[str] = region_option,
|
|
222
|
+
availability_zone: Optional[str] = zone_option,
|
|
223
|
+
cert_type: Optional[CertType] = typer.Option(
|
|
224
|
+
None,
|
|
225
|
+
help="SSL certificate type. Falls to 'custom' "
|
|
226
|
+
"if --cert-data and --cert-key set.",
|
|
227
|
+
),
|
|
228
|
+
cert_domain: Optional[str] = typer.Option(
|
|
229
|
+
None,
|
|
230
|
+
help="Domain name for which the certificate was issued. "
|
|
231
|
+
"Note: domain name A-record will set to load balancer's public IP.",
|
|
232
|
+
),
|
|
233
|
+
cert_data: Optional[typer.FileText] = typer.Option(
|
|
234
|
+
None, help="Fullchain certificate file."
|
|
235
|
+
),
|
|
236
|
+
cert_key: Optional[typer.FileText] = typer.Option(
|
|
237
|
+
None, help="Certificate key file."
|
|
238
|
+
),
|
|
191
239
|
):
|
|
192
240
|
"""Create load balancer."""
|
|
193
241
|
client = create_client(config, profile)
|
|
194
242
|
|
|
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
243
|
payload = {
|
|
211
244
|
"name": name,
|
|
245
|
+
"comment": desc,
|
|
212
246
|
"preset_id": preset_id,
|
|
213
247
|
"algo": algo,
|
|
214
248
|
"proto": proto,
|
|
@@ -222,21 +256,108 @@ def balancer_create(
|
|
|
222
256
|
"proxy_protocol": proxy_protocol,
|
|
223
257
|
"force_https": force_https,
|
|
224
258
|
"backend_keepalive": backend_keepalive,
|
|
259
|
+
"network": {},
|
|
260
|
+
"project_id": project_id,
|
|
261
|
+
"certificates": {},
|
|
225
262
|
}
|
|
226
263
|
|
|
264
|
+
if cert_type == CertType.CUSTOM:
|
|
265
|
+
if not cert_data or not cert_key:
|
|
266
|
+
sys.exit(
|
|
267
|
+
"Error: --cert-data and --cert-key is required if --cert-type is 'custom'"
|
|
268
|
+
)
|
|
269
|
+
elif cert_type == CertType.LETS_ENCRYPT:
|
|
270
|
+
if cert_data or cert_key:
|
|
271
|
+
sys.exit(
|
|
272
|
+
"Error: --cert-data and --cert-key is not allowed with --cert-type 'lets_encrypt'"
|
|
273
|
+
)
|
|
274
|
+
if not cert_type:
|
|
275
|
+
if cert_data and not cert_key:
|
|
276
|
+
sys.exit("Error: --cert-key is required.")
|
|
277
|
+
if cert_key and not cert_data:
|
|
278
|
+
sys.exit("Error: --cert-data is required.")
|
|
279
|
+
if cert_data and cert_key:
|
|
280
|
+
cert_type = CertType.CUSTOM
|
|
281
|
+
if cert_type and not cert_domain:
|
|
282
|
+
sys.exit(
|
|
283
|
+
"Error: --cert-domain is required if --cert-type and/or --cert-data, --cert-key is set."
|
|
284
|
+
)
|
|
285
|
+
if cert_type:
|
|
286
|
+
payload["certificates"] = {
|
|
287
|
+
"type": cert_type.value,
|
|
288
|
+
"fqdn": cert_domain,
|
|
289
|
+
**({"cert_data": cert_data.read()} if cert_data else {}),
|
|
290
|
+
**({"key_data": cert_key.read()} if cert_key else {}),
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if not preset_id:
|
|
294
|
+
for preset in client.get_load_balancer_presets().json()[
|
|
295
|
+
"balancers_presets"
|
|
296
|
+
]:
|
|
297
|
+
if (
|
|
298
|
+
preset["replica_count"] == replicas
|
|
299
|
+
and preset["location"] == region
|
|
300
|
+
):
|
|
301
|
+
preset_id = preset["id"]
|
|
302
|
+
if not preset_id:
|
|
303
|
+
sys.exit(f"Error: Cannot set {replicas} load balancer replicas.")
|
|
304
|
+
|
|
227
305
|
if network:
|
|
228
|
-
|
|
306
|
+
print(
|
|
307
|
+
"Option --network is deprecated and will be removed soon, "
|
|
308
|
+
"use --network-id instead",
|
|
309
|
+
file=sys.stderr,
|
|
310
|
+
)
|
|
311
|
+
network_id = network
|
|
312
|
+
|
|
313
|
+
if network_id:
|
|
314
|
+
payload["network"]["id"] = network_id
|
|
315
|
+
if private_ip:
|
|
316
|
+
net = IPv4Network(
|
|
317
|
+
client.get_vpc(network_id).json()["vpc"]["subnet_v4"]
|
|
318
|
+
)
|
|
319
|
+
if IPv4Address(private_ip) >= net.network_address + 4:
|
|
320
|
+
payload["network"]["ip"] = private_ip
|
|
321
|
+
else:
|
|
322
|
+
# First 3 addresses is reserved by Timeweb Cloud for gateway and future use.
|
|
323
|
+
sys.exit(
|
|
324
|
+
f"Error: Private address '{private_ip}' is not allowed. "
|
|
325
|
+
"IP must be at least the fourth in order in the network."
|
|
326
|
+
)
|
|
327
|
+
if public_ip:
|
|
328
|
+
try:
|
|
329
|
+
_ = IPv4Address(public_ip)
|
|
330
|
+
payload["network"]["floating_ip"] = public_ip
|
|
331
|
+
except ValueError:
|
|
332
|
+
sys.exit(f"Error: '{public_ip}' is not valid IPv4 address.")
|
|
333
|
+
else:
|
|
334
|
+
# Get new public IPv4 address.
|
|
335
|
+
if no_public_ip is False:
|
|
336
|
+
zone = None
|
|
337
|
+
if not preset_id and not availability_zone and not region:
|
|
338
|
+
sys.exit(
|
|
339
|
+
"Error: Unable to get IPv4 address, at least one of "
|
|
340
|
+
"[--preset-id, --region, --availability-zone] "
|
|
341
|
+
"must be set to determine correct location."
|
|
342
|
+
)
|
|
343
|
+
if region:
|
|
344
|
+
zone = REGION_ZONE_MAP[region]
|
|
345
|
+
if preset_id and not availability_zone:
|
|
346
|
+
for preset in client.get_database_presets().json()[
|
|
347
|
+
"databases_presets"
|
|
348
|
+
]:
|
|
349
|
+
if preset["id"] == preset_id:
|
|
350
|
+
zone = REGION_ZONE_MAP[preset["location"]]
|
|
351
|
+
if availability_zone:
|
|
352
|
+
zone = availability_zone
|
|
353
|
+
ip = client.create_floating_ip(availability_zone=zone).json()[
|
|
354
|
+
"ip"
|
|
355
|
+
]["ip"]
|
|
356
|
+
payload["network"]["floating_ip"] = ip
|
|
229
357
|
|
|
230
358
|
# Create LB
|
|
231
359
|
response = client.create_load_balancer(**payload)
|
|
232
360
|
|
|
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
361
|
fmt.printer(
|
|
241
362
|
response,
|
|
242
363
|
output_format=output_format,
|
|
@@ -384,6 +505,64 @@ def blancer_backend_list(
|
|
|
384
505
|
)
|
|
385
506
|
|
|
386
507
|
|
|
508
|
+
# ------------------------------------------------------------- #
|
|
509
|
+
# $ twc balancer list-presets #
|
|
510
|
+
# ------------------------------------------------------------- #
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
def _print_presets(response: Response, filters: Optional[str] = None):
|
|
514
|
+
presets = response.json()["balancers_presets"]
|
|
515
|
+
if filters:
|
|
516
|
+
presets = fmt.filter_list(presets, filters)
|
|
517
|
+
table = fmt.Table()
|
|
518
|
+
table.header(
|
|
519
|
+
[
|
|
520
|
+
"ID",
|
|
521
|
+
"REGION",
|
|
522
|
+
"PRICE",
|
|
523
|
+
"REPLICAS",
|
|
524
|
+
"BANDW",
|
|
525
|
+
"RPS",
|
|
526
|
+
]
|
|
527
|
+
)
|
|
528
|
+
for preset in presets:
|
|
529
|
+
table.row(
|
|
530
|
+
[
|
|
531
|
+
preset["id"],
|
|
532
|
+
preset["location"],
|
|
533
|
+
preset["price"],
|
|
534
|
+
preset["replica_count"],
|
|
535
|
+
preset["bandwidth"],
|
|
536
|
+
preset["request_per_second"],
|
|
537
|
+
]
|
|
538
|
+
)
|
|
539
|
+
table.print()
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
@balancer.command("list-presets", "lp")
|
|
543
|
+
def balancer_list_presets(
|
|
544
|
+
verbose: Optional[bool] = verbose_option,
|
|
545
|
+
config: Optional[Path] = config_option,
|
|
546
|
+
profile: Optional[str] = profile_option,
|
|
547
|
+
output_format: Optional[str] = output_format_option,
|
|
548
|
+
filters: Optional[str] = filter_option,
|
|
549
|
+
region: Optional[ServiceRegion] = typer.Option(
|
|
550
|
+
None, help="Use region (location)."
|
|
551
|
+
),
|
|
552
|
+
):
|
|
553
|
+
"""List configuration presets."""
|
|
554
|
+
if region:
|
|
555
|
+
filters = f"{filters},location:{region}"
|
|
556
|
+
client = create_client(config, profile)
|
|
557
|
+
response = client.get_load_balancer_presets()
|
|
558
|
+
fmt.printer(
|
|
559
|
+
response,
|
|
560
|
+
output_format=output_format,
|
|
561
|
+
filters=filters,
|
|
562
|
+
func=_print_presets,
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
|
|
387
566
|
# ------------------------------------------------------------- #
|
|
388
567
|
# $ twc balancer backend add #
|
|
389
568
|
# ------------------------------------------------------------- #
|
|
@@ -392,7 +571,7 @@ def blancer_backend_list(
|
|
|
392
571
|
@balancer_backend.command("add")
|
|
393
572
|
def blancer_backend_add(
|
|
394
573
|
balancer_id: int = typer.Argument(...),
|
|
395
|
-
backends: List[str] = typer.Argument(..., metavar="
|
|
574
|
+
backends: List[str] = typer.Argument(..., metavar="BACKEND_IP..."),
|
|
396
575
|
verbose: Optional[bool] = verbose_option,
|
|
397
576
|
config: Optional[Path] = config_option,
|
|
398
577
|
profile: Optional[str] = profile_option,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|