twc-cli 1.1.0__py3-none-any.whl → 1.3.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 +48 -15
- twc/__main__.py +72 -1
- twc/__version__.py +1 -1
- twc/api/client.py +319 -2
- twc/click_ext.py +34 -0
- twc/commands/__init__.py +10 -43
- twc/commands/config.py +141 -22
- twc/commands/database.py +633 -0
- twc/commands/image.py +2 -2
- twc/commands/project.py +9 -0
- twc/commands/server.py +89 -47
- twc/commands/storage.py +818 -0
- twc/fmt.py +14 -1
- twc/utils.py +20 -0
- twc/vars.py +20 -0
- {twc_cli-1.1.0.dist-info → twc_cli-1.3.0.dist-info}/METADATA +2 -1
- twc_cli-1.3.0.dist-info/RECORD +26 -0
- {twc_cli-1.1.0.dist-info → twc_cli-1.3.0.dist-info}/WHEEL +1 -1
- twc_cli-1.1.0.dist-info/RECORD +0 -21
- {twc_cli-1.1.0.dist-info → twc_cli-1.3.0.dist-info}/COPYING +0 -0
- {twc_cli-1.1.0.dist-info → twc_cli-1.3.0.dist-info}/entry_points.txt +0 -0
twc/commands/database.py
ADDED
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
"""Database management commands."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
from click_aliases import ClickAliasedGroup
|
|
8
|
+
|
|
9
|
+
from twc import fmt
|
|
10
|
+
from twc.utils import merge_dicts
|
|
11
|
+
from . import (
|
|
12
|
+
create_client,
|
|
13
|
+
handle_request,
|
|
14
|
+
set_value_from_config,
|
|
15
|
+
options,
|
|
16
|
+
debug,
|
|
17
|
+
GLOBAL_OPTIONS,
|
|
18
|
+
OUTPUT_FORMAT_OPTION,
|
|
19
|
+
)
|
|
20
|
+
from .project import (
|
|
21
|
+
get_default_project_id,
|
|
22
|
+
_project_list,
|
|
23
|
+
_project_resource_move,
|
|
24
|
+
_project_resource_list_databases,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@handle_request
|
|
29
|
+
def _database_list(client, *args, **kwargs):
|
|
30
|
+
return client.get_databases(*args, **kwargs)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@handle_request
|
|
34
|
+
def _database_get(client, *args, **kwargs):
|
|
35
|
+
return client.get_database(*args, **kwargs)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@handle_request
|
|
39
|
+
def _database_create(client, *args, **kwargs):
|
|
40
|
+
return client.create_database(*args, **kwargs)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@handle_request
|
|
44
|
+
def _database_remove(client, *args, **kwargs):
|
|
45
|
+
return client.delete_database(*args, **kwargs)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@handle_request
|
|
49
|
+
def _database_set(client, *args, **kwargs):
|
|
50
|
+
return client.update_database(*args, **kwargs)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@handle_request
|
|
54
|
+
def _database_backup_list(client, *args, **kwargs):
|
|
55
|
+
return client.get_database_backups(*args, **kwargs)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@handle_request
|
|
59
|
+
def _database_backup_create(client, *args, **kwargs):
|
|
60
|
+
return client.create_database_backup(*args, **kwargs)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@handle_request
|
|
64
|
+
def _database_backup_remove(client, *args, **kwargs):
|
|
65
|
+
return client.delete_database_backup(*args, **kwargs)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@handle_request
|
|
69
|
+
def _database_backup_restore(client, *args, **kwargs):
|
|
70
|
+
return client.restore_database_backup(*args, **kwargs)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@handle_request
|
|
74
|
+
def _database_list_presets(client, *args, **kwargs):
|
|
75
|
+
return client.get_database_presets(*args, **kwargs)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# ------------------------------------------------------------- #
|
|
79
|
+
# $ twc database #
|
|
80
|
+
# ------------------------------------------------------------- #
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@click.group("database", cls=ClickAliasedGroup)
|
|
84
|
+
@options(GLOBAL_OPTIONS[:2])
|
|
85
|
+
def database():
|
|
86
|
+
"""Manage databases."""
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# ------------------------------------------------------------- #
|
|
90
|
+
# $ twc database list #
|
|
91
|
+
# ------------------------------------------------------------- #
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def print_databases(response: object, filters: str):
|
|
95
|
+
if filters:
|
|
96
|
+
dbs = fmt.filter_list(response.json()["dbs"], filters)
|
|
97
|
+
else:
|
|
98
|
+
dbs = response.json()["dbs"]
|
|
99
|
+
|
|
100
|
+
table = fmt.Table()
|
|
101
|
+
table.header(
|
|
102
|
+
[
|
|
103
|
+
"ID",
|
|
104
|
+
"NAME",
|
|
105
|
+
"STATUS",
|
|
106
|
+
"TYPE",
|
|
107
|
+
"IPV4",
|
|
108
|
+
"INTERNAL IP",
|
|
109
|
+
]
|
|
110
|
+
)
|
|
111
|
+
for db in dbs:
|
|
112
|
+
table.row(
|
|
113
|
+
[
|
|
114
|
+
db["id"],
|
|
115
|
+
db["name"],
|
|
116
|
+
db["status"],
|
|
117
|
+
db["type"],
|
|
118
|
+
db["ip"],
|
|
119
|
+
db["local_ip"],
|
|
120
|
+
]
|
|
121
|
+
)
|
|
122
|
+
table.print()
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@database.command("list", aliases=["ls"], help="List databases.")
|
|
126
|
+
@options(GLOBAL_OPTIONS)
|
|
127
|
+
@options(OUTPUT_FORMAT_OPTION)
|
|
128
|
+
@click.option(
|
|
129
|
+
"--limit",
|
|
130
|
+
type=int,
|
|
131
|
+
default=500,
|
|
132
|
+
show_default=True,
|
|
133
|
+
help="Items to display.",
|
|
134
|
+
)
|
|
135
|
+
@click.option("--filter", "-f", "filters", default="", help="Filter output.")
|
|
136
|
+
def database_list(config, profile, verbose, output_format, limit, filters):
|
|
137
|
+
client = create_client(config, profile)
|
|
138
|
+
response = _database_list(client, limit=limit)
|
|
139
|
+
fmt.printer(
|
|
140
|
+
response,
|
|
141
|
+
output_format=output_format,
|
|
142
|
+
filters=filters,
|
|
143
|
+
func=print_databases,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# ------------------------------------------------------------- #
|
|
148
|
+
# $ twc database get #
|
|
149
|
+
# ------------------------------------------------------------- #
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def print_database(response: object):
|
|
153
|
+
db = response.json()["db"]
|
|
154
|
+
table = fmt.Table()
|
|
155
|
+
table.header(
|
|
156
|
+
[
|
|
157
|
+
"ID",
|
|
158
|
+
"NAME",
|
|
159
|
+
"STATUS",
|
|
160
|
+
"TYPE",
|
|
161
|
+
"IPV4",
|
|
162
|
+
"INTERNAL IP",
|
|
163
|
+
]
|
|
164
|
+
)
|
|
165
|
+
table.row(
|
|
166
|
+
[
|
|
167
|
+
db["id"],
|
|
168
|
+
db["name"],
|
|
169
|
+
db["status"],
|
|
170
|
+
db["type"],
|
|
171
|
+
db["ip"],
|
|
172
|
+
db["local_ip"],
|
|
173
|
+
]
|
|
174
|
+
)
|
|
175
|
+
table.print()
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@database.command("get", help="Get database.")
|
|
179
|
+
@options(GLOBAL_OPTIONS)
|
|
180
|
+
@options(OUTPUT_FORMAT_OPTION)
|
|
181
|
+
@click.option(
|
|
182
|
+
"--status",
|
|
183
|
+
is_flag=True,
|
|
184
|
+
help="Display status and exit with 0 if status is 'started'.",
|
|
185
|
+
)
|
|
186
|
+
@click.argument("db_id", type=int, required=True)
|
|
187
|
+
def database_get(config, profile, verbose, output_format, status, db_id):
|
|
188
|
+
client = create_client(config, profile)
|
|
189
|
+
response = _database_get(client, db_id)
|
|
190
|
+
if status:
|
|
191
|
+
_status = response.json()["db"]["status"]
|
|
192
|
+
click.echo(_status)
|
|
193
|
+
if _status == "started":
|
|
194
|
+
sys.exit(0)
|
|
195
|
+
else:
|
|
196
|
+
sys.exit(1)
|
|
197
|
+
fmt.printer(
|
|
198
|
+
response,
|
|
199
|
+
output_format=output_format,
|
|
200
|
+
func=print_database,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
# ------------------------------------------------------------- #
|
|
205
|
+
# $ twc database list-presets #
|
|
206
|
+
# ------------------------------------------------------------- #
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def print_dbs_presets(response: object, filters: str):
|
|
210
|
+
if filters:
|
|
211
|
+
presets = fmt.filter_list(
|
|
212
|
+
response.json()["databases_presets"], filters
|
|
213
|
+
)
|
|
214
|
+
else:
|
|
215
|
+
presets = response.json()["databases_presets"]
|
|
216
|
+
|
|
217
|
+
table = fmt.Table()
|
|
218
|
+
table.header(
|
|
219
|
+
[
|
|
220
|
+
"ID",
|
|
221
|
+
"REGION",
|
|
222
|
+
"PRICE",
|
|
223
|
+
"CPU",
|
|
224
|
+
"RAM",
|
|
225
|
+
"DISK",
|
|
226
|
+
"TYPE",
|
|
227
|
+
]
|
|
228
|
+
)
|
|
229
|
+
for preset in presets:
|
|
230
|
+
table.row(
|
|
231
|
+
[
|
|
232
|
+
preset["id"],
|
|
233
|
+
preset["location"],
|
|
234
|
+
preset["price"],
|
|
235
|
+
preset["cpu"],
|
|
236
|
+
str(round(preset["ram"] / 1024)) + "G",
|
|
237
|
+
str(round(preset["disk"] / 1024)) + "G",
|
|
238
|
+
preset["type"],
|
|
239
|
+
]
|
|
240
|
+
)
|
|
241
|
+
table.print()
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@database.command(
|
|
245
|
+
"list-presets", aliases=["lp"], help="List database presets."
|
|
246
|
+
)
|
|
247
|
+
@options(GLOBAL_OPTIONS)
|
|
248
|
+
@options(OUTPUT_FORMAT_OPTION)
|
|
249
|
+
@click.option("--filter", "-f", "filters", default="", help="Filter output.")
|
|
250
|
+
@click.option("--region", help="Use region (location).")
|
|
251
|
+
def database_list_presets(
|
|
252
|
+
config, profile, verbose, output_format, filters, region
|
|
253
|
+
):
|
|
254
|
+
if filters:
|
|
255
|
+
filters = filters.replace("region", "location")
|
|
256
|
+
if region:
|
|
257
|
+
if filters:
|
|
258
|
+
filters = filters + f",location:{region}"
|
|
259
|
+
else:
|
|
260
|
+
filters = f"location:{region}"
|
|
261
|
+
|
|
262
|
+
client = create_client(config, profile)
|
|
263
|
+
response = _database_list_presets(client)
|
|
264
|
+
fmt.printer(
|
|
265
|
+
response,
|
|
266
|
+
output_format=output_format,
|
|
267
|
+
filters=filters,
|
|
268
|
+
func=print_dbs_presets,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
# ------------------------------------------------------------- #
|
|
273
|
+
# $ twc database create #
|
|
274
|
+
# ------------------------------------------------------------- #
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def set_params(params: tuple) -> dict:
|
|
278
|
+
parameters = {}
|
|
279
|
+
for param in params:
|
|
280
|
+
if re.match(r"^([a-z_]+)=([0-9a-zA-Z]+)$", param):
|
|
281
|
+
parameter, value = param.split("=")
|
|
282
|
+
if value.isdigit():
|
|
283
|
+
value = int(value)
|
|
284
|
+
parameters[parameter] = value
|
|
285
|
+
else:
|
|
286
|
+
raise click.BadParameter(
|
|
287
|
+
f"'{param}': Parameter can contain only digits,"
|
|
288
|
+
" latin letters and underscore."
|
|
289
|
+
)
|
|
290
|
+
return parameters
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
@database.command("create", help="Create new database.")
|
|
294
|
+
@options(GLOBAL_OPTIONS)
|
|
295
|
+
@options(OUTPUT_FORMAT_OPTION)
|
|
296
|
+
@click.option(
|
|
297
|
+
"--preset-id", type=int, required=True, help="Database preset ID."
|
|
298
|
+
)
|
|
299
|
+
@click.option("--name", required=True, help="Database display name.")
|
|
300
|
+
@click.option(
|
|
301
|
+
"--type",
|
|
302
|
+
"dbms",
|
|
303
|
+
type=click.Choice(["mysql8", "mysql5", "postgres", "redis", "mongodb"]),
|
|
304
|
+
required=True,
|
|
305
|
+
help="DBMS.",
|
|
306
|
+
)
|
|
307
|
+
@click.option(
|
|
308
|
+
"--hash-type",
|
|
309
|
+
type=click.Choice(["caching_sha2", "mysql_native"]),
|
|
310
|
+
default="caching_sha2",
|
|
311
|
+
show_default=True,
|
|
312
|
+
help="Authentication plugin for MySQL.",
|
|
313
|
+
)
|
|
314
|
+
@click.option(
|
|
315
|
+
"--param",
|
|
316
|
+
"params",
|
|
317
|
+
multiple=True,
|
|
318
|
+
help="Database parameters, can be multiple.",
|
|
319
|
+
)
|
|
320
|
+
@click.option(
|
|
321
|
+
"--project-id",
|
|
322
|
+
type=int,
|
|
323
|
+
default=None,
|
|
324
|
+
envvar="TWC_PROJECT",
|
|
325
|
+
callback=set_value_from_config,
|
|
326
|
+
help="Add database to specific project.",
|
|
327
|
+
)
|
|
328
|
+
@click.option("--login", default=None, help="Database user login.")
|
|
329
|
+
@click.option(
|
|
330
|
+
"--password",
|
|
331
|
+
prompt="Set database user password",
|
|
332
|
+
hide_input=True,
|
|
333
|
+
confirmation_prompt=True,
|
|
334
|
+
help="Database user password.",
|
|
335
|
+
)
|
|
336
|
+
def database_create(
|
|
337
|
+
config,
|
|
338
|
+
profile,
|
|
339
|
+
verbose,
|
|
340
|
+
output_format,
|
|
341
|
+
preset_id,
|
|
342
|
+
name,
|
|
343
|
+
dbms,
|
|
344
|
+
hash_type,
|
|
345
|
+
params,
|
|
346
|
+
login,
|
|
347
|
+
password,
|
|
348
|
+
project_id,
|
|
349
|
+
):
|
|
350
|
+
# pylint: disable=too-many-locals
|
|
351
|
+
|
|
352
|
+
client = create_client(config, profile)
|
|
353
|
+
|
|
354
|
+
if dbms == "mysql8": # alias mysql8 for mysql
|
|
355
|
+
dbms = "mysql"
|
|
356
|
+
|
|
357
|
+
# Check preset_id
|
|
358
|
+
for preset in _database_list_presets(client).json()["databases_presets"]:
|
|
359
|
+
if preset["id"] == preset_id:
|
|
360
|
+
_dbms = re.sub(
|
|
361
|
+
r"[0-9]+", "", dbms
|
|
362
|
+
) # transform 'mysql5' to 'mysql', etc.
|
|
363
|
+
if not preset["type"].startswith(_dbms):
|
|
364
|
+
sys.exit(
|
|
365
|
+
f"Error: DBMS '{dbms}' doesn't match with preset_id type."
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
payload = {
|
|
369
|
+
"dbms": dbms,
|
|
370
|
+
"preset_id": preset_id,
|
|
371
|
+
"name": name,
|
|
372
|
+
"hash_type": hash_type,
|
|
373
|
+
"login": login,
|
|
374
|
+
"password": password,
|
|
375
|
+
"config_parameters": {},
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if params:
|
|
379
|
+
payload["config_parameters"] = set_params(params)
|
|
380
|
+
|
|
381
|
+
if project_id:
|
|
382
|
+
debug("Check project_id")
|
|
383
|
+
projects = _project_list(client).json()["projects"]
|
|
384
|
+
if not project_id in [prj["id"] for prj in projects]:
|
|
385
|
+
raise click.BadParameter("Wrong project ID.")
|
|
386
|
+
|
|
387
|
+
response = _database_create(client, **payload)
|
|
388
|
+
|
|
389
|
+
# Add created DB to project if set
|
|
390
|
+
if project_id:
|
|
391
|
+
src_project = get_default_project_id(client)
|
|
392
|
+
# Make useless request to avoid API bug (409 resource_not_found)
|
|
393
|
+
_r = _project_resource_list_databases(client, src_project)
|
|
394
|
+
new_db_id = response.json()["db"]["id"]
|
|
395
|
+
debug(f"Add DB '{new_db_id}' to project '{project_id}'")
|
|
396
|
+
project_resp = _project_resource_move(
|
|
397
|
+
client,
|
|
398
|
+
from_project=src_project,
|
|
399
|
+
to_project=project_id,
|
|
400
|
+
resource_id=new_db_id,
|
|
401
|
+
resource_type="database",
|
|
402
|
+
)
|
|
403
|
+
debug(project_resp.text)
|
|
404
|
+
|
|
405
|
+
fmt.printer(
|
|
406
|
+
response,
|
|
407
|
+
output_format=output_format,
|
|
408
|
+
func=lambda response: click.echo(response.json()["db"]["id"]),
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
# ------------------------------------------------------------- #
|
|
413
|
+
# $ twc database set #
|
|
414
|
+
# ------------------------------------------------------------- #
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
@database.command("set", help="Set database properties or parameters.")
|
|
418
|
+
@options(GLOBAL_OPTIONS)
|
|
419
|
+
@options(OUTPUT_FORMAT_OPTION)
|
|
420
|
+
@click.option(
|
|
421
|
+
"--preset-id", type=int, default=None, help="Database preset ID."
|
|
422
|
+
)
|
|
423
|
+
@click.option("--name", default=None, help="Database display name.")
|
|
424
|
+
@click.option(
|
|
425
|
+
"--param",
|
|
426
|
+
"params",
|
|
427
|
+
metavar="PARAMETER=VALUE",
|
|
428
|
+
multiple=True,
|
|
429
|
+
default=None,
|
|
430
|
+
help="Database parameters, can be multiple.",
|
|
431
|
+
)
|
|
432
|
+
@click.option(
|
|
433
|
+
"--password",
|
|
434
|
+
default=None,
|
|
435
|
+
help="Database user password.",
|
|
436
|
+
)
|
|
437
|
+
@click.option(
|
|
438
|
+
"--prompt-password",
|
|
439
|
+
is_flag=True,
|
|
440
|
+
help="Set database user password interactively.",
|
|
441
|
+
)
|
|
442
|
+
@click.option(
|
|
443
|
+
"--external-ip",
|
|
444
|
+
type=bool,
|
|
445
|
+
default=None,
|
|
446
|
+
help="Enable external IPv4 address.",
|
|
447
|
+
)
|
|
448
|
+
@click.argument("db_id", type=int, required=True)
|
|
449
|
+
def database_set(
|
|
450
|
+
config,
|
|
451
|
+
profile,
|
|
452
|
+
verbose,
|
|
453
|
+
output_format,
|
|
454
|
+
preset_id,
|
|
455
|
+
name,
|
|
456
|
+
params,
|
|
457
|
+
password,
|
|
458
|
+
prompt_password,
|
|
459
|
+
external_ip,
|
|
460
|
+
db_id,
|
|
461
|
+
):
|
|
462
|
+
# pylint: disable=too-many-locals
|
|
463
|
+
|
|
464
|
+
client = create_client(config, profile)
|
|
465
|
+
old_state = _database_get(client, db_id).json()["db"]
|
|
466
|
+
new_params = {}
|
|
467
|
+
|
|
468
|
+
if prompt_password:
|
|
469
|
+
password = click.prompt(
|
|
470
|
+
"Set database user password",
|
|
471
|
+
hide_input=True,
|
|
472
|
+
confirmation_prompt=True,
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
payload = {
|
|
476
|
+
"preset_id": preset_id,
|
|
477
|
+
"name": name,
|
|
478
|
+
"password": password,
|
|
479
|
+
"config_parameters": {},
|
|
480
|
+
"external_ip": external_ip,
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if params:
|
|
484
|
+
new_params = set_params(params)
|
|
485
|
+
|
|
486
|
+
if new_params:
|
|
487
|
+
payload["config_parameters"] = merge_dicts(
|
|
488
|
+
old_state["config_parameters"], new_params
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
response = _database_set(client, db_id, **payload)
|
|
492
|
+
fmt.printer(
|
|
493
|
+
response,
|
|
494
|
+
output_format=output_format,
|
|
495
|
+
func=lambda response: click.echo(response.json()["db"]["id"]),
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
# ------------------------------------------------------------- #
|
|
500
|
+
# $ twc database remove #
|
|
501
|
+
# ------------------------------------------------------------- #
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
@database.command("remove", aliases=["rm"], help="Remove database.")
|
|
505
|
+
@options(GLOBAL_OPTIONS)
|
|
506
|
+
@click.confirmation_option(prompt="This action cannot be undone. Continue?")
|
|
507
|
+
@click.argument("db_ids", nargs=-1, type=int, required=True)
|
|
508
|
+
def database_remove(config, profile, verbose, db_ids):
|
|
509
|
+
client = create_client(config, profile)
|
|
510
|
+
for db_id in db_ids:
|
|
511
|
+
response = _database_remove(client, db_id)
|
|
512
|
+
|
|
513
|
+
if response.status_code == 200:
|
|
514
|
+
del_hash = response.json()["database_delete"]["hash"]
|
|
515
|
+
del_code = click.prompt("Please enter confirmation code", type=int)
|
|
516
|
+
response = _database_remove(
|
|
517
|
+
client, db_id, delete_hash=del_hash, code=del_code
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
if response.status_code == 204:
|
|
521
|
+
click.echo(db_id)
|
|
522
|
+
else:
|
|
523
|
+
fmt.printer(response)
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
# ------------------------------------------------------------- #
|
|
527
|
+
# $ twc database backup #
|
|
528
|
+
# ------------------------------------------------------------- #
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
@database.group("backup", cls=ClickAliasedGroup)
|
|
532
|
+
@options(GLOBAL_OPTIONS[:2])
|
|
533
|
+
def db_backup():
|
|
534
|
+
"""Manage database backups."""
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
# ------------------------------------------------------------- #
|
|
538
|
+
# $ twc database backup list #
|
|
539
|
+
# ------------------------------------------------------------- #
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def print_db_backups(response: object):
|
|
543
|
+
backups = response.json()["backups"]
|
|
544
|
+
table = fmt.Table()
|
|
545
|
+
table.header(
|
|
546
|
+
[
|
|
547
|
+
"ID",
|
|
548
|
+
"DISK",
|
|
549
|
+
"CREATED",
|
|
550
|
+
"STATUS",
|
|
551
|
+
]
|
|
552
|
+
)
|
|
553
|
+
for bak in backups:
|
|
554
|
+
table.row(
|
|
555
|
+
[
|
|
556
|
+
bak["id"],
|
|
557
|
+
bak["name"],
|
|
558
|
+
bak["created_at"],
|
|
559
|
+
bak["status"],
|
|
560
|
+
]
|
|
561
|
+
)
|
|
562
|
+
table.print()
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
@db_backup.command("list", aliases=["ls"], help="List backups.")
|
|
566
|
+
@options(GLOBAL_OPTIONS)
|
|
567
|
+
@options(OUTPUT_FORMAT_OPTION)
|
|
568
|
+
@click.argument("db_id", type=int, required=True)
|
|
569
|
+
def database_backup_list(config, profile, verbose, output_format, db_id):
|
|
570
|
+
client = create_client(config, profile)
|
|
571
|
+
response = _database_backup_list(client, db_id)
|
|
572
|
+
fmt.printer(
|
|
573
|
+
response,
|
|
574
|
+
output_format=output_format,
|
|
575
|
+
func=print_db_backups,
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
# ------------------------------------------------------------- #
|
|
580
|
+
# $ twc database backup create #
|
|
581
|
+
# ------------------------------------------------------------- #
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
@db_backup.command("create", help="Backup database.")
|
|
585
|
+
@options(GLOBAL_OPTIONS)
|
|
586
|
+
@options(OUTPUT_FORMAT_OPTION)
|
|
587
|
+
@click.argument("db_id", type=int, required=True)
|
|
588
|
+
def database_backup_create(config, profile, verbose, output_format, db_id):
|
|
589
|
+
client = create_client(config, profile)
|
|
590
|
+
response = _database_backup_create(client, db_id)
|
|
591
|
+
fmt.printer(
|
|
592
|
+
response,
|
|
593
|
+
output_format=output_format,
|
|
594
|
+
func=lambda response: click.echo(response.json()["backup"]["id"]),
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
# ------------------------------------------------------------- #
|
|
599
|
+
# $ twc database backup remove #
|
|
600
|
+
# ------------------------------------------------------------- #
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
@db_backup.command("remove", aliases=["rm"], help="Remove backup.")
|
|
604
|
+
@options(GLOBAL_OPTIONS)
|
|
605
|
+
@click.argument("db_id", type=int, required=True)
|
|
606
|
+
@click.argument("backup_id", type=int, required=True)
|
|
607
|
+
@click.confirmation_option(prompt="This action cannot be undone. Continue?")
|
|
608
|
+
def database_backup_remove(config, profile, verbose, db_id, backup_id):
|
|
609
|
+
client = create_client(config, profile)
|
|
610
|
+
response = _database_backup_remove(client, db_id, backup_id)
|
|
611
|
+
if response.status_code == 204:
|
|
612
|
+
click.echo(backup_id)
|
|
613
|
+
else:
|
|
614
|
+
fmt.printer(response)
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
# ------------------------------------------------------------- #
|
|
618
|
+
# $ twc database backup restore #
|
|
619
|
+
# ------------------------------------------------------------- #
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
@db_backup.command("restore", help="Remove backup.")
|
|
623
|
+
@options(GLOBAL_OPTIONS)
|
|
624
|
+
@click.argument("db_id", type=int, required=True)
|
|
625
|
+
@click.argument("backup_id", type=int, required=True)
|
|
626
|
+
@click.confirmation_option(prompt="Data on target disk will lost. Continue?")
|
|
627
|
+
def database_backup_restore(config, profile, verbose, db_id, backup_id):
|
|
628
|
+
client = create_client(config, profile)
|
|
629
|
+
response = _database_backup_restore(client, db_id, backup_id)
|
|
630
|
+
if response.status_code == 204:
|
|
631
|
+
click.echo(backup_id)
|
|
632
|
+
else:
|
|
633
|
+
fmt.printer(response)
|
twc/commands/image.py
CHANGED
|
@@ -271,7 +271,7 @@ def draw_progressbar(monitor):
|
|
|
271
271
|
print("Bytes:", monitor.bytes_read)
|
|
272
272
|
|
|
273
273
|
|
|
274
|
-
@image.command("upload", help="Upload image from
|
|
274
|
+
@image.command("upload", help="Upload image from URL.")
|
|
275
275
|
@options(GLOBAL_OPTIONS)
|
|
276
276
|
@options(OUTPUT_FORMAT_OPTION)
|
|
277
277
|
@click.option(
|
|
@@ -302,7 +302,7 @@ def draw_progressbar(monitor):
|
|
|
302
302
|
)
|
|
303
303
|
@click.option(
|
|
304
304
|
"--location",
|
|
305
|
-
type=click.Choice(["ru-1", "ru-2", "pl-1", "kz-1"]),
|
|
305
|
+
type=click.Choice(["ru-1", "ru-2", "pl-1", "kz-1", "nl-1"]),
|
|
306
306
|
default="ru-1",
|
|
307
307
|
show_default=True,
|
|
308
308
|
help="Region to upload image.",
|
twc/commands/project.py
CHANGED
|
@@ -10,6 +10,7 @@ from . import (
|
|
|
10
10
|
create_client,
|
|
11
11
|
handle_request,
|
|
12
12
|
options,
|
|
13
|
+
debug,
|
|
13
14
|
GLOBAL_OPTIONS,
|
|
14
15
|
OUTPUT_FORMAT_OPTION,
|
|
15
16
|
)
|
|
@@ -91,6 +92,14 @@ def _project_resource_move(client, *args, **kwargs):
|
|
|
91
92
|
return client.move_resource_to_project(*args, **kwargs)
|
|
92
93
|
|
|
93
94
|
|
|
95
|
+
def get_default_project_id(client):
|
|
96
|
+
for prj in _project_list(client).json()["projects"]:
|
|
97
|
+
if prj["is_default"]:
|
|
98
|
+
debug(f"Default project ID is {prj['id']}")
|
|
99
|
+
return prj["id"]
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
|
|
94
103
|
# ------------------------------------------------------------- #
|
|
95
104
|
# $ twc project #
|
|
96
105
|
# ------------------------------------------------------------- #
|