vantage6 5.0.0a22__py3-none-any.whl → 5.0.0a29__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 (55) hide show
  1. tests_cli/test_client_script.py +23 -0
  2. tests_cli/test_server_cli.py +7 -6
  3. tests_cli/test_wizard.py +7 -7
  4. vantage6/cli/__build__ +1 -1
  5. vantage6/cli/algorithm/generate_algorithm_json.py +531 -0
  6. vantage6/cli/algostore/list.py +2 -1
  7. vantage6/cli/algostore/start.py +6 -6
  8. vantage6/cli/algostore/stop.py +3 -2
  9. vantage6/cli/cli.py +25 -0
  10. vantage6/cli/common/decorator.py +3 -1
  11. vantage6/cli/common/start.py +221 -12
  12. vantage6/cli/common/stop.py +90 -0
  13. vantage6/cli/common/utils.py +15 -20
  14. vantage6/cli/config.py +260 -0
  15. vantage6/cli/configuration_manager.py +8 -14
  16. vantage6/cli/configuration_wizard.py +66 -111
  17. vantage6/cli/context/__init__.py +2 -1
  18. vantage6/cli/context/algorithm_store.py +10 -7
  19. vantage6/cli/context/node.py +38 -54
  20. vantage6/cli/context/server.py +36 -5
  21. vantage6/cli/dev/create.py +88 -29
  22. vantage6/cli/dev/data/km_dataset.csv +2401 -0
  23. vantage6/cli/dev/remove.py +99 -98
  24. vantage6/cli/globals.py +24 -4
  25. vantage6/cli/node/common/__init__.py +6 -5
  26. vantage6/cli/node/new.py +4 -3
  27. vantage6/cli/node/remove.py +4 -2
  28. vantage6/cli/node/start.py +33 -42
  29. vantage6/cli/prometheus/monitoring_manager.py +146 -0
  30. vantage6/cli/prometheus/prometheus.yml +5 -0
  31. vantage6/cli/server/files.py +4 -2
  32. vantage6/cli/server/import_.py +7 -7
  33. vantage6/cli/server/list.py +2 -1
  34. vantage6/cli/server/new.py +25 -6
  35. vantage6/cli/server/shell.py +5 -4
  36. vantage6/cli/server/start.py +44 -213
  37. vantage6/cli/server/stop.py +36 -105
  38. vantage6/cli/server/version.py +5 -4
  39. vantage6/cli/template/algo_store_config.j2 +0 -1
  40. vantage6/cli/template/node_config.j2 +3 -1
  41. vantage6/cli/template/server_import_config.j2 +0 -2
  42. vantage6/cli/test/algo_test_scripts/algo_test_arguments.py +29 -0
  43. vantage6/cli/test/algo_test_scripts/algo_test_script.py +92 -0
  44. vantage6/cli/test/client_script.py +151 -0
  45. vantage6/cli/test/common/diagnostic_runner.py +2 -2
  46. vantage6/cli/test/feature_tester.py +5 -2
  47. vantage6/cli/use/context.py +46 -0
  48. vantage6/cli/use/namespace.py +55 -0
  49. vantage6/cli/utils.py +70 -4
  50. {vantage6-5.0.0a22.dist-info → vantage6-5.0.0a29.dist-info}/METADATA +15 -11
  51. vantage6-5.0.0a29.dist-info/RECORD +84 -0
  52. {vantage6-5.0.0a22.dist-info → vantage6-5.0.0a29.dist-info}/WHEEL +1 -1
  53. vantage6-5.0.0a22.dist-info/RECORD +0 -72
  54. {vantage6-5.0.0a22.dist-info → vantage6-5.0.0a29.dist-info}/entry_points.txt +0 -0
  55. {vantage6-5.0.0a22.dist-info → vantage6-5.0.0a29.dist-info}/top_level.txt +0 -0
@@ -2,23 +2,20 @@ from __future__ import annotations
2
2
 
3
3
  import hashlib
4
4
  import os.path
5
-
6
5
  from pathlib import Path
7
6
 
8
7
  from vantage6.common.context import AppContext
9
8
  from vantage6.common.globals import APPNAME, STRING_ENCODING, InstanceType
9
+
10
+ from vantage6.cli._version import __version__
10
11
  from vantage6.cli.configuration_manager import NodeConfigurationManager
11
12
  from vantage6.cli.globals import DEFAULT_NODE_SYSTEM_FOLDERS as N_FOL
12
- from vantage6.cli._version import __version__
13
13
 
14
14
 
15
15
  class NodeContext(AppContext):
16
16
  """
17
17
  Node context object for the host system.
18
18
 
19
- See DockerNodeContext for the node instance mounts when running as a
20
- dockerized service.
21
-
22
19
  Parameters
23
20
  ----------
24
21
  instance_name : str
@@ -43,12 +40,7 @@ class NodeContext(AppContext):
43
40
  logger_prefix: str = "",
44
41
  ):
45
42
  super().__init__(
46
- InstanceType.NODE,
47
- instance_name,
48
- system_folders,
49
- config_file,
50
- print_log_header,
51
- logger_prefix,
43
+ InstanceType.NODE, instance_name, system_folders=system_folders
52
44
  )
53
45
  if print_log_header:
54
46
  self.log.info("vantage6 version '%s'", __version__)
@@ -136,7 +128,7 @@ class NodeContext(AppContext):
136
128
  Path
137
129
  Path to the data folder
138
130
  """
139
- return AppContext.type_data_folder(InstanceType.NODE.value, system_folders)
131
+ return AppContext.type_data_folder(InstanceType.NODE, system_folders)
140
132
 
141
133
  @property
142
134
  def databases(self) -> dict:
@@ -188,48 +180,6 @@ class NodeContext(AppContext):
188
180
  """
189
181
  return os.environ.get("DATA_VOLUME_NAME", f"{self.docker_container_name}-vol")
190
182
 
191
- @property
192
- def docker_vpn_volume_name(self) -> str:
193
- """
194
- Docker volume in which the VPN configuration is stored.
195
-
196
- Returns
197
- -------
198
- str
199
- Docker volume name
200
- """
201
- return os.environ.get(
202
- "VPN_VOLUME_NAME", f"{self.docker_container_name}-vpn-vol"
203
- )
204
-
205
- @property
206
- def docker_ssh_volume_name(self) -> str:
207
- """
208
- Docker volume in which the SSH configuration is stored.
209
-
210
- Returns
211
- -------
212
- str
213
- Docker volume name
214
- """
215
- return os.environ.get(
216
- "SSH_TUNNEL_VOLUME_NAME", f"{self.docker_container_name}-ssh-vol"
217
- )
218
-
219
- @property
220
- def docker_squid_volume_name(self) -> str:
221
- """
222
- Docker volume in which the SSH configuration is stored.
223
-
224
- Returns
225
- -------
226
- str
227
- Docker volume name
228
- """
229
- return os.environ.get(
230
- "SSH_SQUID_VOLUME_NAME", f"{self.docker_container_name}-squid-vol"
231
- )
232
-
233
183
  @property
234
184
  def proxy_log_file(self):
235
185
  return self.log_file_name(type_="proxy_server")
@@ -250,6 +200,40 @@ class NodeContext(AppContext):
250
200
  """
251
201
  return self.config["databases"][label]
252
202
 
203
+ def set_folders(
204
+ self, instance_type: InstanceType, instance_name: str, system_folders: bool
205
+ ) -> None:
206
+ """
207
+ Set the folders for the node.
208
+
209
+ Parameters
210
+ ----------
211
+ instance_type : InstanceType
212
+ Instance type
213
+ instance_name : str
214
+ Instance name
215
+ system_folders : bool
216
+ Whether to use system folders
217
+ """
218
+ dirs = self.instance_folders(instance_type, instance_name, system_folders)
219
+
220
+ self.log_dir = dirs.get("log")
221
+ self.data_dir = dirs.get("data")
222
+ self.config_dir = dirs.get("config")
223
+
224
+ @staticmethod
225
+ def instance_folders(*_args, **_kwargs) -> dict:
226
+ """Log, data and config folders are always mounted. The node manager
227
+ should take care of this.
228
+ """
229
+ mnt = Path("/mnt")
230
+
231
+ return {
232
+ "log": mnt / "log",
233
+ "data": mnt / "data",
234
+ "config": mnt / "config",
235
+ }
236
+
253
237
  def __create_node_identifier(self) -> str:
254
238
  """
255
239
  Create a unique identifier for the node.
@@ -1,14 +1,18 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from pathlib import Path
4
+
3
5
  from vantage6.common.globals import APPNAME, InstanceType
6
+
7
+ from vantage6.cli._version import __version__
4
8
  from vantage6.cli.configuration_manager import ServerConfigurationManager
9
+ from vantage6.cli.context.base_server import BaseServerContext
5
10
  from vantage6.cli.globals import (
6
11
  DEFAULT_SERVER_SYSTEM_FOLDERS as S_FOL,
7
- ServerType,
12
+ PROMETHEUS_DIR,
8
13
  ServerGlobals,
14
+ ServerType,
9
15
  )
10
- from vantage6.cli._version import __version__
11
- from vantage6.cli.context.base_server import BaseServerContext
12
16
 
13
17
 
14
18
  class ServerContext(BaseServerContext):
@@ -43,7 +47,7 @@ class ServerContext(BaseServerContext):
43
47
  str
44
48
  string representation of the database uri
45
49
  """
46
- return super().get_database_uri(ServerGlobals.DB_URI_ENV_VAR)
50
+ return super().get_database_uri(ServerGlobals.DB_URI_ENV_VAR.value)
47
51
 
48
52
  @property
49
53
  def docker_container_name(self) -> str:
@@ -57,6 +61,30 @@ class ServerContext(BaseServerContext):
57
61
  """
58
62
  return f"{APPNAME}-{self.name}-{self.scope}-{ServerType.V6SERVER}"
59
63
 
64
+ @property
65
+ def prometheus_container_name(self) -> str:
66
+ """
67
+ Get the name of the Prometheus Docker container for this server.
68
+
69
+ Returns
70
+ -------
71
+ str
72
+ Prometheus container name, unique to this server instance.
73
+ """
74
+ return f"{APPNAME}-{self.name}-{self.scope}-prometheus"
75
+
76
+ @property
77
+ def prometheus_dir(self) -> Path:
78
+ """
79
+ Get the Prometheus directory path.
80
+
81
+ Returns
82
+ -------
83
+ Path
84
+ Path to the Prometheus directory
85
+ """
86
+ return self.data_dir / PROMETHEUS_DIR
87
+
60
88
  @classmethod
61
89
  def from_external_config_file(
62
90
  cls, path: str, system_folders: bool = S_FOL
@@ -79,7 +107,10 @@ class ServerContext(BaseServerContext):
79
107
  Server context object
80
108
  """
81
109
  return super().from_external_config_file(
82
- path, ServerType.V6SERVER, ServerGlobals.CONFIG_NAME_ENV_VAR, system_folders
110
+ path,
111
+ ServerType.V6SERVER,
112
+ ServerGlobals.CONFIG_NAME_ENV_VAR.value,
113
+ system_folders,
83
114
  )
84
115
 
85
116
  @classmethod
@@ -1,26 +1,28 @@
1
- from pathlib import Path
2
1
  from importlib import resources as impresources
3
- import yaml
2
+ from pathlib import Path
3
+
4
4
  import click
5
5
  import pandas as pd
6
-
7
- from jinja2 import Environment, FileSystemLoader
6
+ import yaml
8
7
  from colorama import Fore, Style
8
+ from jinja2 import Environment, FileSystemLoader
9
9
 
10
+ from vantage6.common import ensure_config_dir_writable, error, generate_apikey, info
10
11
  from vantage6.common.globals import APPNAME, InstanceType, Ports
11
- from vantage6.common import ensure_config_dir_writable, info, error, generate_apikey
12
12
 
13
13
  import vantage6.cli.dev.data as data_dir
14
14
  from vantage6.cli.context.algorithm_store import AlgorithmStoreContext
15
- from vantage6.cli.globals import PACKAGE_FOLDER
16
- from vantage6.cli.context.server import ServerContext
17
15
  from vantage6.cli.context.node import NodeContext
16
+ from vantage6.cli.context.server import ServerContext
17
+ from vantage6.cli.globals import PACKAGE_FOLDER, DefaultDatasets
18
18
  from vantage6.cli.server.common import get_server_context
19
19
  from vantage6.cli.server.import_ import cli_server_import
20
20
  from vantage6.cli.utils import prompt_config_name
21
21
 
22
22
 
23
- def create_node_data_files(num_nodes: int, server_name: str) -> list[Path]:
23
+ def create_node_data_files(
24
+ num_nodes: int, server_name: str, dataset: tuple[str, Path]
25
+ ) -> list[tuple[str, Path]]:
24
26
  """Create data files for nodes.
25
27
 
26
28
  Parameters
@@ -29,15 +31,16 @@ def create_node_data_files(num_nodes: int, server_name: str) -> list[Path]:
29
31
  Number of nodes to create data files for.
30
32
  server_name : str
31
33
  Name of the server.
32
-
34
+ dataset : tuple[str, Path]
35
+ Tuple containing the name and the path to the dataset.
33
36
  Returns
34
37
  -------
35
- list[Path]
36
- List of paths to the created data files.
38
+ list[tuple[str, Path]]
39
+ List of the label and paths to the created data files.
37
40
  """
38
41
  info(f"Creating data files for {num_nodes} nodes.")
39
42
  data_files = []
40
- full_df = pd.read_csv(impresources.files(data_dir) / "olympic_athletes_2016.csv")
43
+ full_df = pd.read_csv(dataset[1])
41
44
  length_df = len(full_df)
42
45
  for i in range(num_nodes):
43
46
  node_name = f"{server_name}_node_{i + 1}"
@@ -49,16 +52,20 @@ def create_node_data_files(num_nodes: int, server_name: str) -> list[Path]:
49
52
  start = i * length_df // num_nodes
50
53
  end = (i + 1) * length_df // num_nodes
51
54
  data = full_df[start:end]
52
- data_file = data_folder / f"df_{node_name}.csv"
55
+ data_file = data_folder / f"df_{dataset[0]}_{node_name}.csv"
53
56
 
54
57
  # write data to file
55
58
  data.to_csv(data_file, index=False)
56
- data_files.append(data_file)
59
+ data_files.append((dataset[0], data_file))
57
60
  return data_files
58
61
 
59
62
 
60
63
  def create_node_config_file(
61
- server_url: str, port: int, config: dict, server_name: str, datafile: Path
64
+ server_url: str,
65
+ port: int,
66
+ config: dict,
67
+ server_name: str,
68
+ datasets: list[tuple[str, Path]] = (),
62
69
  ) -> None:
63
70
  """Create a node configuration file (YAML).
64
71
 
@@ -77,8 +84,8 @@ def create_node_config_file(
77
84
  additional user_defined_config.
78
85
  server_name : str
79
86
  Configuration name of the dummy server.
80
- datafile : Path
81
- Path to the data file for the node to use.
87
+ datasets : list[tuple[str, Path]]
88
+ List of tuples containing the labels and the paths to the datasets
82
89
  """
83
90
  environment = Environment(
84
91
  loader=FileSystemLoader(PACKAGE_FOLDER / APPNAME / "cli" / "template"),
@@ -102,10 +109,12 @@ def create_node_config_file(
102
109
  error(f"Node configuration file already exists: {full_path}")
103
110
  exit(1)
104
111
 
112
+ databases = [{dataset[0]: dataset[1]} for dataset in datasets]
113
+
105
114
  node_config = template.render(
106
115
  {
107
116
  "api_key": config["api_key"],
108
- "databases": {"default": datafile},
117
+ "databases": databases,
109
118
  "logging": {"file": f"{node_name}.log"},
110
119
  "port": port,
111
120
  "server_url": server_url,
@@ -124,8 +133,7 @@ def create_node_config_file(
124
133
  f.write(node_config)
125
134
 
126
135
  info(
127
- f"Spawned node for organization {Fore.GREEN}{config['org_id']}"
128
- f"{Style.RESET_ALL}"
136
+ f"Spawned node for organization {Fore.GREEN}{config['org_id']}{Style.RESET_ALL}"
129
137
  )
130
138
 
131
139
 
@@ -156,6 +164,7 @@ def generate_node_configs(
156
164
  port: int,
157
165
  server_name: str,
158
166
  extra_node_config: Path | None,
167
+ extra_datasets: list[tuple[str, Path]],
159
168
  ) -> list[dict]:
160
169
  """Generates ``num_nodes`` node configuration files.
161
170
 
@@ -171,6 +180,8 @@ def generate_node_configs(
171
180
  Configuration name of the dummy server.
172
181
  extra_node_config : Path | None
173
182
  Path to file with additional node configuration.
183
+ extra_datasets : list[tuple[str, Path]]
184
+ List of tuples containing the labels and the paths to extra datasets
174
185
 
175
186
  Returns
176
187
  -------
@@ -178,8 +189,36 @@ def generate_node_configs(
178
189
  List of dictionaries containing node configurations.
179
190
  """
180
191
  configs = []
192
+ node_data_files = []
181
193
  extra_config = _read_extra_config_file(extra_node_config)
182
- node_data_files = create_node_data_files(num_nodes, server_name)
194
+
195
+ data_directory = impresources.files(data_dir)
196
+
197
+ # Add default datasets to the list of dataset provided
198
+ for default_dataset in DefaultDatasets:
199
+ extra_datasets.append(
200
+ (default_dataset.name.lower(), data_directory / default_dataset.value)
201
+ )
202
+
203
+ # Check for duplicate dataset labels
204
+ seen_labels = set()
205
+ duplicates = [
206
+ label
207
+ for label in [dataset[0] for dataset in extra_datasets]
208
+ if (label in seen_labels or seen_labels.add(label))
209
+ ]
210
+
211
+ if len(duplicates) > 0:
212
+ error(
213
+ f"Duplicate dataset labels found: {duplicates}. "
214
+ f"Please make sure all dataset labels are unique."
215
+ )
216
+ exit(1)
217
+
218
+ # create the data files for the nodes and get the path and label for each dataset
219
+ for dataset in extra_datasets:
220
+ node_data_files.append(create_node_data_files(num_nodes, server_name, dataset))
221
+
183
222
  for i in range(num_nodes):
184
223
  config = {
185
224
  "org_id": i + 1,
@@ -188,7 +227,11 @@ def generate_node_configs(
188
227
  "user_defined_config": extra_config,
189
228
  }
190
229
  create_node_config_file(
191
- server_url, port, config, server_name, node_data_files[i]
230
+ server_url,
231
+ port,
232
+ config,
233
+ server_name,
234
+ [files[i] for files in node_data_files],
192
235
  )
193
236
  configs.append(config)
194
237
 
@@ -385,7 +428,7 @@ def create_algo_store_config(
385
428
  )
386
429
  folders = AlgorithmStoreContext.instance_folders(
387
430
  instance_type=InstanceType.ALGORITHM_STORE,
388
- instance_name="{server_name}_store",
431
+ instance_name=f"{server_name}_store",
389
432
  system_folders=False,
390
433
  )
391
434
 
@@ -421,6 +464,7 @@ def demo_network(
421
464
  ui_image: str,
422
465
  ui_port: int,
423
466
  algorithm_store_port: int,
467
+ extra_datasets: list[tuple[str, Path]],
424
468
  ) -> tuple[list[dict], Path, Path]:
425
469
  """Generates the demo network.
426
470
 
@@ -447,6 +491,8 @@ def demo_network(
447
491
  Port to run the UI on.
448
492
  algorithm_store_port : int
449
493
  Port to run the algorithm store on.
494
+ extra_datasets : list[tuple[str, Path]]
495
+ List of tuples containing the labels and the paths to extra datasets
450
496
 
451
497
  Returns
452
498
  -------
@@ -454,7 +500,12 @@ def demo_network(
454
500
  Tuple containing node, server import and server configurations.
455
501
  """
456
502
  node_configs = generate_node_configs(
457
- num_nodes, server_url, server_port, server_name, extra_node_config
503
+ num_nodes,
504
+ server_url,
505
+ server_port,
506
+ server_name,
507
+ extra_node_config,
508
+ extra_datasets,
458
509
  )
459
510
  server_import_config = create_vserver_import_config(node_configs, server_name)
460
511
  server_config = create_vserver_config(
@@ -495,21 +546,19 @@ def demo_network(
495
546
  "--server-port",
496
547
  type=int,
497
548
  default=Ports.DEV_SERVER.value,
498
- help=f"Port to run the server on. Default is {Ports.DEV_SERVER.value}.",
549
+ help=f"Port to run the server on. Default is {Ports.DEV_SERVER}.",
499
550
  )
500
551
  @click.option(
501
552
  "--ui-port",
502
553
  type=int,
503
554
  default=Ports.DEV_UI.value,
504
- help=f"Port to run the UI on. Default is {Ports.DEV_UI.value}.",
555
+ help=f"Port to run the UI on. Default is {Ports.DEV_UI}.",
505
556
  )
506
557
  @click.option(
507
558
  "--algorithm-store-port",
508
559
  type=int,
509
560
  default=Ports.DEV_ALGO_STORE.value,
510
- help=(
511
- f"Port to run the algorithm store on. Default is {Ports.DEV_ALGO_STORE.value}."
512
- ),
561
+ help=(f"Port to run the algorithm store on. Default is {Ports.DEV_ALGO_STORE}."),
513
562
  )
514
563
  @click.option(
515
564
  "-i",
@@ -548,6 +597,14 @@ def demo_network(
548
597
  help="YAML File with additional algorithm store configuration. This will be"
549
598
  " appended to the algorithm store configuration file",
550
599
  )
600
+ @click.option(
601
+ "--add-dataset",
602
+ type=(str, click.Path()),
603
+ default=(),
604
+ multiple=True,
605
+ help="Add a dataset to the nodes. The first argument is the label of the database, "
606
+ "the second is the path to the dataset file.",
607
+ )
551
608
  @click.pass_context
552
609
  def create_demo_network(
553
610
  click_ctx: click.Context,
@@ -562,6 +619,7 @@ def create_demo_network(
562
619
  extra_server_config: Path = None,
563
620
  extra_node_config: Path = None,
564
621
  extra_store_config: Path = None,
622
+ add_dataset: list[tuple[str, Path]] = (),
565
623
  ) -> dict:
566
624
  """Creates a demo network.
567
625
 
@@ -583,6 +641,7 @@ def create_demo_network(
583
641
  ui_image,
584
642
  ui_port,
585
643
  algorithm_store_port,
644
+ list(add_dataset),
586
645
  )
587
646
  info(
588
647
  f"Created {Fore.GREEN}{len(demo[0])}{Style.RESET_ALL} node "