vantage6 4.0.3__py3-none-any.whl → 4.1.0b0__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 vantage6 might be problematic. Click here for more details.

Files changed (39) hide show
  1. tests_cli/test_node_cli.py +60 -55
  2. tests_cli/test_server_cli.py +30 -30
  3. vantage6/cli/_version.py +1 -1
  4. vantage6/cli/cli.py +102 -0
  5. vantage6/cli/{dev.py → dev/create.py} +18 -151
  6. vantage6/cli/dev/remove.py +63 -0
  7. vantage6/cli/dev/start.py +52 -0
  8. vantage6/cli/dev/stop.py +30 -0
  9. vantage6/cli/node/attach.py +58 -0
  10. vantage6/cli/node/clean.py +40 -0
  11. vantage6/cli/node/common/__init__.py +124 -0
  12. vantage6/cli/node/create_private_key.py +139 -0
  13. vantage6/cli/node/files.py +34 -0
  14. vantage6/cli/node/list.py +62 -0
  15. vantage6/cli/node/new.py +46 -0
  16. vantage6/cli/node/remove.py +103 -0
  17. vantage6/cli/node/set_api_key.py +45 -0
  18. vantage6/cli/node/start.py +311 -0
  19. vantage6/cli/node/stop.py +73 -0
  20. vantage6/cli/node/version.py +47 -0
  21. vantage6/cli/server/attach.py +54 -0
  22. vantage6/cli/server/common/__init__.py +146 -0
  23. vantage6/cli/server/files.py +16 -0
  24. vantage6/cli/server/import_.py +144 -0
  25. vantage6/cli/server/list.py +60 -0
  26. vantage6/cli/server/new.py +50 -0
  27. vantage6/cli/server/shell.py +42 -0
  28. vantage6/cli/server/start.py +302 -0
  29. vantage6/cli/server/stop.py +158 -0
  30. vantage6/cli/server/version.py +46 -0
  31. {vantage6-4.0.3.dist-info → vantage6-4.1.0b0.dist-info}/METADATA +7 -6
  32. vantage6-4.1.0b0.dist-info/RECORD +52 -0
  33. vantage6-4.1.0b0.dist-info/entry_points.txt +5 -0
  34. vantage6/cli/node.py +0 -1097
  35. vantage6/cli/server.py +0 -1033
  36. vantage6-4.0.3.dist-info/RECORD +0 -28
  37. vantage6-4.0.3.dist-info/entry_points.txt +0 -4
  38. {vantage6-4.0.3.dist-info → vantage6-4.1.0b0.dist-info}/WHEEL +0 -0
  39. {vantage6-4.0.3.dist-info → vantage6-4.1.0b0.dist-info}/top_level.txt +0 -0
@@ -1,37 +1,18 @@
1
- """
2
- This module contains the CLI commands for generating dummy server and node
3
- instance(s). The following commands are available:
4
-
5
- * vdev create-demo-network
6
- * vdev remove-demo-network
7
- * vdev start-demo-network
8
- * vdev stop-demo-network
9
- """
10
- import click
1
+ from pathlib import Path
11
2
  import csv
12
- import subprocess
13
- import itertools
14
3
 
15
- from pathlib import Path
4
+ import click
16
5
  from jinja2 import Environment, FileSystemLoader
17
6
  from colorama import Fore, Style
18
- from shutil import rmtree
19
7
 
20
8
  from vantage6.common.globals import APPNAME
21
9
  from vantage6.common import info, error, generate_apikey
22
10
 
23
11
  from vantage6.cli.globals import PACKAGE_FOLDER
24
12
  from vantage6.cli.context import ServerContext, NodeContext
25
- from vantage6.cli.server import (
26
- click_insert_context,
27
- vserver_import,
28
- vserver_start,
29
- vserver_stop,
30
- vserver_remove,
31
- get_server_context
32
- )
33
- from vantage6.cli.node import vnode_stop
34
- from vantage6.cli.utils import prompt_config_name, remove_file
13
+ from vantage6.cli.server.common import get_server_context
14
+ from vantage6.cli.server.import_ import cli_server_import
15
+ from vantage6.cli.utils import prompt_config_name
35
16
 
36
17
 
37
18
  def create_dummy_data(node_name: str, dev_folder: Path) -> Path:
@@ -172,9 +153,9 @@ def generate_node_configs(num_nodes: int, server_url: str, port: int,
172
153
 
173
154
  def create_vserver_import_config(node_configs: list[dict], server_name: str) \
174
155
  -> Path:
175
- """Creates vserver configuration import file (YAML).
156
+ """Create server configuration import file (YAML).
176
157
 
177
- Utilized by the ``vserver import`` command.
158
+ Utilized by the ``v6 server import`` command.
178
159
 
179
160
  Parameters
180
161
  ----------
@@ -231,7 +212,7 @@ def create_vserver_import_config(node_configs: list[dict], server_name: str) \
231
212
 
232
213
 
233
214
  def create_vserver_config(server_name: str, port: int) -> Path:
234
- """Creates vserver configuration file (YAML).
215
+ """Creates server configuration file (YAML).
235
216
 
236
217
  Parameters
237
218
  ----------
@@ -304,15 +285,7 @@ def demo_network(num_nodes: int, server_url: str, server_port: int,
304
285
  return (node_configs, server_import_config, server_config)
305
286
 
306
287
 
307
- @click.group(name="dev")
308
- def cli_dev() -> None:
309
- """
310
- The `vdev` commands can be used to quickly manage a network with a server
311
- and several nodes for local testing.
312
- """
313
-
314
-
315
- @cli_dev.command(name="create-demo-network")
288
+ @click.command()
316
289
  @click.option('-n', '--name', default=None, type=str,
317
290
  help="Name for your development setup")
318
291
  @click.option('--num-nodes', type=int, default=3,
@@ -325,8 +298,11 @@ def cli_dev() -> None:
325
298
  @click.option('-i', '--image', type=str, default=None,
326
299
  help='Server docker image to use when setting up resources for '
327
300
  'the development server')
328
- def create_demo_network(name: str, num_nodes: int, server_url: str,
329
- server_port: int, image: str = None) -> dict:
301
+ @click.pass_context
302
+ def create_demo_network(
303
+ click_ctx: click.Context, name: str, num_nodes: int, server_url: str,
304
+ server_port: int, image: str = None
305
+ ) -> dict:
330
306
  """Creates a demo network.
331
307
 
332
308
  Creates server instance as well as its import configuration file. Server
@@ -346,121 +322,12 @@ def create_demo_network(name: str, num_nodes: int, server_url: str,
346
322
  exit(1)
347
323
  (node_config, server_import_config, server_config) = demo
348
324
  ctx = get_server_context(server_name, True)
349
- vserver_import(ctx, server_import_config, False, image, '', False, True)
325
+ click_ctx.invoke(
326
+ cli_server_import, ctx=ctx, file=server_import_config, drop_all=False,
327
+ image=image, mount_src='', keep=False, wait=True
328
+ )
350
329
  return {
351
330
  "node_configs": node_config,
352
331
  "server_import_config": server_import_config,
353
332
  "server_config": server_config
354
333
  }
355
-
356
-
357
- @cli_dev.command(name="start-demo-network")
358
- @click_insert_context
359
- @click.option('--server-image', type=str, default=None,
360
- help='Server Docker image to use')
361
- @click.option('--node-image', type=str, default=None,
362
- help='Node Docker image to use')
363
- def start_demo_network(
364
- ctx: ServerContext, server_image: str, node_image: str
365
- ) -> None:
366
- """Starts running a demo-network.
367
-
368
- Select a server configuration to run its demo network. You should choose a
369
- server configuration that you created earlier for a demo network. If you
370
- have not created a demo network, you can run `vdev create-demo-network` to
371
- create one.
372
- """
373
- # run the server
374
- vserver_start(
375
- ctx=ctx,
376
- ip=None,
377
- port=None,
378
- image=server_image,
379
- start_ui=False,
380
- ui_port=None,
381
- start_rabbitmq=False,
382
- rabbitmq_image=None,
383
- keep=True,
384
- mount_src='',
385
- attach=False
386
- )
387
-
388
- # run all nodes that belong to this server
389
- configs, _ = NodeContext.available_configurations(system_folders=False)
390
- node_names = [
391
- config.name for config in configs if f'{ctx.name}_node_' in config.name
392
- ]
393
- for name in node_names:
394
- cmd = ["vnode", "start", "--name", name]
395
- if node_image:
396
- cmd.extend(["--image", node_image])
397
- subprocess.run(cmd)
398
-
399
-
400
- @cli_dev.command(name="stop-demo-network")
401
- @click_insert_context
402
- def stop_demo_network(ctx: ServerContext) -> None:
403
- """ Stops a demo network's server and nodes.
404
-
405
- Select a server configuration to stop that server and the nodes attached
406
- to it.
407
- """
408
- # stop the server
409
- vserver_stop(name=ctx.name, system_folders=True, all_servers=False)
410
-
411
- # stop the nodes
412
- configs, _ = NodeContext.available_configurations(False)
413
- node_names = [
414
- config.name for config in configs if f'{ctx.name}_node_' in config.name
415
- ]
416
- for name in node_names:
417
- vnode_stop(name, system_folders=False, all_nodes=False, force=False)
418
-
419
-
420
- @cli_dev.command(name="remove-demo-network")
421
- @click_insert_context
422
- @click.option('-f', "--force", type=bool, flag_value=True,
423
- help='Don\'t ask for confirmation')
424
- def remove_demo_network(ctx: ServerContext, force: bool) -> None:
425
- """ Remove all related demo network files and folders.
426
-
427
- Select a server configuration to remove that server and the nodes attached
428
- to it.
429
- """
430
-
431
- # remove the server
432
- for handler in itertools.chain(ctx.log.handlers, ctx.log.root.handlers):
433
- handler.close()
434
- vserver_remove(ctx, ctx.name, True, force)
435
-
436
- # removing the server import config
437
- info("Deleting demo import config file")
438
- server_configs = ServerContext.instance_folders("server", ctx.name,
439
- system_folders=False)
440
- import_config_to_del = Path(server_configs['dev']) / f"{ctx.name}.yaml"
441
- remove_file(import_config_to_del, 'import_configuration')
442
-
443
- # also remove the server folder
444
- server_configs = ServerContext.instance_folders("server", ctx.name,
445
- system_folders=True)
446
- server_folder = server_configs['data']
447
- if server_folder.is_dir():
448
- rmtree(server_folder)
449
- # TODO BvB 2023-07-31 can it happen that the server folder is not a
450
- # directory? What then?
451
-
452
- # remove the nodes
453
- configs, _ = NodeContext.available_configurations(system_folders=False)
454
- node_names = [
455
- config.name for config in configs if f'{ctx.name}_node_' in config.name
456
- ]
457
- for name in node_names:
458
- node_ctx = NodeContext(name, False)
459
- for handler in itertools.chain(node_ctx.log.handlers,
460
- node_ctx.log.root.handlers):
461
- handler.close()
462
- subprocess.run(["vnode", "remove", "-n", name, "--user", "--force"])
463
-
464
- # remove data files attached to the network
465
- data_dirs_nodes = NodeContext.instance_folders('node', '', False)['dev']
466
- rmtree(Path(data_dirs_nodes / ctx.name))
@@ -0,0 +1,63 @@
1
+ import subprocess
2
+ import itertools
3
+ from shutil import rmtree
4
+ from pathlib import Path
5
+
6
+ import click
7
+
8
+ from vantage6.common import info
9
+ from vantage6.cli.context import ServerContext, NodeContext
10
+ from vantage6.cli.server.common import click_insert_context
11
+ from vantage6.cli.server.stop import vserver_remove
12
+ from vantage6.cli.utils import remove_file
13
+
14
+
15
+ @click.command()
16
+ @click_insert_context
17
+ @click.option('-f', "--force", type=bool, flag_value=True,
18
+ help='Don\'t ask for confirmation')
19
+ def remove_demo_network(ctx: ServerContext, force: bool) -> None:
20
+ """ Remove all related demo network files and folders.
21
+
22
+ Select a server configuration to remove that server and the nodes attached
23
+ to it.
24
+ """
25
+
26
+ # remove the server
27
+ for handler in itertools.chain(ctx.log.handlers, ctx.log.root.handlers):
28
+ handler.close()
29
+ vserver_remove(ctx, ctx.name, True, force)
30
+
31
+ # removing the server import config
32
+ info("Deleting demo import config file")
33
+ server_configs = ServerContext.instance_folders("server", ctx.name,
34
+ system_folders=False)
35
+ import_config_to_del = Path(server_configs['dev']) / f"{ctx.name}.yaml"
36
+ remove_file(import_config_to_del, 'import_configuration')
37
+
38
+ # also remove the server folder
39
+ server_configs = ServerContext.instance_folders("server", ctx.name,
40
+ system_folders=True)
41
+ server_folder = server_configs['data']
42
+ if server_folder.is_dir():
43
+ rmtree(server_folder)
44
+ # TODO BvB 2023-07-31 can it happen that the server folder is not a
45
+ # directory? What then?
46
+
47
+ # remove the nodes
48
+ configs, _ = NodeContext.available_configurations(system_folders=False)
49
+ node_names = [
50
+ config.name for config in configs if f'{ctx.name}_node_' in config.name
51
+ ]
52
+ for name in node_names:
53
+ node_ctx = NodeContext(name, False)
54
+ for handler in itertools.chain(node_ctx.log.handlers,
55
+ node_ctx.log.root.handlers):
56
+ handler.close()
57
+ subprocess.run(
58
+ ["v6", "node", "remove", "-n", name, "--user", "--force"]
59
+ )
60
+
61
+ # remove data files attached to the network
62
+ data_dirs_nodes = NodeContext.instance_folders('node', '', False)['dev']
63
+ rmtree(Path(data_dirs_nodes / ctx.name))
@@ -0,0 +1,52 @@
1
+ import subprocess
2
+ import click
3
+
4
+ from vantage6.cli.context import ServerContext, NodeContext
5
+ from vantage6.cli.server.common import click_insert_context
6
+ from vantage6.cli.server.start import cli_server_start
7
+
8
+
9
+ @click.command()
10
+ @click_insert_context
11
+ @click.option('--server-image', type=str, default=None,
12
+ help='Server Docker image to use')
13
+ @click.option('--node-image', type=str, default=None,
14
+ help='Node Docker image to use')
15
+ @click.pass_context
16
+ def start_demo_network(
17
+ click_ctx: click.Context, ctx: ServerContext, server_image: str,
18
+ node_image: str
19
+ ) -> None:
20
+ """Starts running a demo-network.
21
+
22
+ Select a server configuration to run its demo network. You should choose a
23
+ server configuration that you created earlier for a demo network. If you
24
+ have not created a demo network, you can run `vdev create-demo-network` to
25
+ create one.
26
+ """
27
+ # run the server
28
+ click_ctx.invoke(
29
+ cli_server_start,
30
+ ctx=ctx,
31
+ ip=None,
32
+ port=None,
33
+ image=server_image,
34
+ start_ui=False,
35
+ ui_port=None,
36
+ start_rabbitmq=False,
37
+ rabbitmq_image=None,
38
+ keep=True,
39
+ mount_src='',
40
+ attach=False
41
+ )
42
+
43
+ # run all nodes that belong to this server
44
+ configs, _ = NodeContext.available_configurations(system_folders=False)
45
+ node_names = [
46
+ config.name for config in configs if f'{ctx.name}_node_' in config.name
47
+ ]
48
+ for name in node_names:
49
+ cmd = ["v6", "node", "start", "--name", name]
50
+ if node_image:
51
+ cmd.extend(["--image", node_image])
52
+ subprocess.run(cmd)
@@ -0,0 +1,30 @@
1
+ import click
2
+
3
+ from vantage6.cli.context import ServerContext, NodeContext
4
+ from vantage6.cli.server.common import click_insert_context
5
+ from vantage6.cli.server.stop import cli_server_stop
6
+ from vantage6.cli.node.stop import cli_node_stop
7
+
8
+
9
+ @click.command()
10
+ @click_insert_context
11
+ @click.pass_context
12
+ def stop_demo_network(click_ctx: click.Context, ctx: ServerContext) -> None:
13
+ """ Stops a demo network's server and nodes.
14
+
15
+ Select a server configuration to stop that server and the nodes attached
16
+ to it.
17
+ """
18
+ # stop the server
19
+ click_ctx.invoke(
20
+ cli_server_stop, name=ctx.name, system_folders=True, all_servers=False
21
+ )
22
+
23
+ # stop the nodes
24
+ configs, _ = NodeContext.available_configurations(False)
25
+ node_names = [
26
+ config.name for config in configs if f'{ctx.name}_node_' in config.name
27
+ ]
28
+ for name in node_names:
29
+ click_ctx.invoke(cli_node_stop, name=name, system_folders=False,
30
+ all_nodes=False, force=False)
@@ -0,0 +1,58 @@
1
+ import time
2
+ from threading import Thread
3
+ import click
4
+ import questionary as q
5
+ import docker
6
+
7
+ from colorama import Fore, Style
8
+
9
+ from vantage6.common import warning, error, info
10
+ from vantage6.common.globals import APPNAME
11
+ from vantage6.common.docker.addons import check_docker_running
12
+
13
+ from vantage6.cli.globals import DEFAULT_NODE_SYSTEM_FOLDERS as N_FOL
14
+ from vantage6.cli.node.common import find_running_node_names, print_log_worker
15
+
16
+
17
+ @click.command()
18
+ @click.option("-n", "--name", default=None, help="Configuration name")
19
+ @click.option('--system', 'system_folders', flag_value=True,
20
+ help="Search for configuration in system folders rather than "
21
+ "user folders")
22
+ @click.option('--user', 'system_folders', flag_value=False, default=N_FOL,
23
+ help="Search for configuration in user folders rather than "
24
+ "system folders. This is the default")
25
+ def cli_node_attach(name: str, system_folders: bool) -> None:
26
+ """
27
+ Show the node logs in the current console.
28
+ """
29
+ check_docker_running()
30
+ client = docker.from_env()
31
+
32
+ running_node_names = find_running_node_names(client)
33
+
34
+ if not running_node_names:
35
+ warning("No nodes are currently running. Cannot show any logs!")
36
+ return
37
+
38
+ if not name:
39
+ name = q.select("Select the node you wish to inspect:",
40
+ choices=running_node_names).ask()
41
+ else:
42
+ post_fix = "system" if system_folders else "user"
43
+ name = f"{APPNAME}-{name}-{post_fix}"
44
+
45
+ if name in running_node_names:
46
+ container = client.containers.get(name)
47
+ logs = container.attach(stream=True, logs=True)
48
+ Thread(target=print_log_worker, args=(logs,), daemon=True).start()
49
+ while True:
50
+ try:
51
+ time.sleep(1)
52
+ except KeyboardInterrupt:
53
+ info("Closing log file. Keyboard Interrupt.")
54
+ info("Note that your node is still running! Shut it down with "
55
+ f"'{Fore.RED}v6 node stop{Style.RESET_ALL}'")
56
+ exit(0)
57
+ else:
58
+ error(f"{Fore.RED}{name}{Style.RESET_ALL} was not running!?")
@@ -0,0 +1,40 @@
1
+ import click
2
+ import questionary as q
3
+ import docker
4
+
5
+ from colorama import Fore, Style
6
+
7
+ from vantage6.common import error, info, debug
8
+ from vantage6.common.docker.addons import check_docker_running
9
+
10
+
11
+ @click.command()
12
+ def cli_node_clean() -> None:
13
+ """
14
+ Erase temporary Docker volumes.
15
+ """
16
+ check_docker_running()
17
+ client = docker.from_env()
18
+
19
+ # retrieve all volumes
20
+ volumes = client.volumes.list()
21
+ candidates = []
22
+ msg = "This would remove the following volumes: "
23
+ for volume in volumes:
24
+ if volume.name[-6:] == "tmpvol":
25
+ candidates.append(volume)
26
+ msg += volume.name + ","
27
+ info(msg)
28
+
29
+ confirm = q.confirm("Are you sure?")
30
+ if confirm.ask():
31
+ for volume in candidates:
32
+ try:
33
+ volume.remove()
34
+ # info(volume.name)
35
+ except docker.errors.APIError as e:
36
+ error(f"Failed to remove volume {Fore.RED}'{volume.name}'"
37
+ f"{Style.RESET_ALL}. Is it still in use?")
38
+ debug(e)
39
+ exit(1)
40
+ info("Done!")
@@ -0,0 +1,124 @@
1
+ """
2
+ Common functions that are used in node CLI commands
3
+ """
4
+ from typing import Iterable
5
+
6
+ import questionary as q
7
+ import docker
8
+ from colorama import Fore, Style
9
+
10
+ from vantage6.common import error, info, debug
11
+ from vantage6.common.globals import STRING_ENCODING, APPNAME
12
+ from vantage6.client import UserClient
13
+
14
+ from vantage6.cli.context import NodeContext
15
+ from vantage6.cli.configuration_wizard import select_configuration_questionaire
16
+
17
+
18
+ # helper functions
19
+ def print_log_worker(logs_stream: Iterable[bytes]) -> None:
20
+ """
21
+ Print the logs from the logs stream.
22
+
23
+ Parameters
24
+ ----------
25
+ logs_stream : Iterable[bytes]
26
+ Output of the container.attach() method
27
+ """
28
+ for log in logs_stream:
29
+ print(log.decode(STRING_ENCODING), end="")
30
+
31
+
32
+ def create_client(ctx: NodeContext) -> UserClient:
33
+ """
34
+ Create a client instance.
35
+
36
+ Parameters
37
+ ----------
38
+ ctx : NodeContext
39
+ Context of the node loaded from the configuration file
40
+ Returns
41
+ -------
42
+ UserClient
43
+ vantage6 client
44
+ """
45
+ host = ctx.config['server_url']
46
+ # if the server is run locally, we need to use localhost here instead of
47
+ # the host address of docker
48
+ if host in ['http://host.docker.internal', 'http://172.17.0.1']:
49
+ host = 'http://localhost'
50
+ port = ctx.config['port']
51
+ api_path = ctx.config['api_path']
52
+ info(f"Connecting to server at '{host}:{port}{api_path}'")
53
+ return UserClient(host, port, api_path, log_level='warn')
54
+
55
+
56
+ def create_client_and_authenticate(ctx: NodeContext) -> UserClient:
57
+ """
58
+ Generate a client and authenticate with the server.
59
+
60
+ Parameters
61
+ ----------
62
+ ctx : NodeContext
63
+ Context of the node loaded from the configuration file
64
+
65
+ Returns
66
+ -------
67
+ UserClient
68
+ vantage6 client
69
+ """
70
+ client = create_client(ctx)
71
+
72
+ username = q.text("Username:").ask()
73
+ password = q.password("Password:").ask()
74
+
75
+ try:
76
+ client.authenticate(username, password)
77
+
78
+ except Exception as exc:
79
+ error("Could not authenticate with server!")
80
+ debug(exc)
81
+ exit(1)
82
+
83
+ return client
84
+
85
+
86
+ def select_node(name: str, system_folders: bool) -> tuple[str, str]:
87
+ """
88
+ Let user select node through questionnaire if name is not given.
89
+
90
+ Returns
91
+ -------
92
+ str
93
+ Name of the configuration file
94
+ """
95
+ name = name if name else \
96
+ select_configuration_questionaire("node", system_folders)
97
+
98
+ # raise error if config could not be found
99
+ if not NodeContext.config_exists(name, system_folders):
100
+ error(
101
+ f"The configuration {Fore.RED}{name}{Style.RESET_ALL} could "
102
+ f"not be found."
103
+ )
104
+ exit(1)
105
+ return name
106
+
107
+
108
+ def find_running_node_names(client: docker.DockerClient) -> list[str]:
109
+ """
110
+ Returns a list of names of running nodes.
111
+
112
+ Parameters
113
+ ----------
114
+ client : docker.DockerClient
115
+ Docker client instance
116
+
117
+ Returns
118
+ -------
119
+ list[str]
120
+ List of names of running nodes
121
+ """
122
+ running_nodes = client.containers.list(
123
+ filters={"label": f"{APPNAME}-type=node"})
124
+ return [node.name for node in running_nodes]