vantage6 5.0.0a21__py3-none-any.whl → 5.0.0a26__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.
- tests_cli/test_client_script.py +23 -0
- vantage6/cli/__build__ +1 -1
- vantage6/cli/algorithm/generate_algorithm_json.py +529 -0
- vantage6/cli/cli.py +25 -0
- vantage6/cli/common/start.py +220 -9
- vantage6/cli/common/stop.py +90 -0
- vantage6/cli/common/utils.py +8 -7
- vantage6/cli/config.py +260 -0
- vantage6/cli/configuration_manager.py +3 -11
- vantage6/cli/configuration_wizard.py +60 -101
- vantage6/cli/context/node.py +34 -45
- vantage6/cli/context/server.py +26 -0
- vantage6/cli/dev/create.py +78 -17
- vantage6/cli/dev/data/km_dataset.csv +2401 -0
- vantage6/cli/dev/remove.py +99 -98
- vantage6/cli/globals.py +20 -0
- vantage6/cli/node/new.py +4 -3
- vantage6/cli/node/remove.py +4 -2
- vantage6/cli/node/start.py +17 -20
- vantage6/cli/prometheus/monitoring_manager.py +146 -0
- vantage6/cli/prometheus/prometheus.yml +5 -0
- vantage6/cli/server/new.py +25 -6
- vantage6/cli/server/start.py +42 -212
- vantage6/cli/server/stop.py +35 -105
- vantage6/cli/template/algo_store_config.j2 +0 -1
- vantage6/cli/template/node_config.j2 +1 -1
- vantage6/cli/template/server_import_config.j2 +0 -2
- vantage6/cli/test/algo_test_scripts/algo_test_arguments.py +29 -0
- vantage6/cli/test/algo_test_scripts/algo_test_script.py +91 -0
- vantage6/cli/test/client_script.py +151 -0
- vantage6/cli/test/common/diagnostic_runner.py +2 -2
- vantage6/cli/use/context.py +46 -0
- vantage6/cli/use/namespace.py +55 -0
- vantage6/cli/utils.py +70 -4
- {vantage6-5.0.0a21.dist-info → vantage6-5.0.0a26.dist-info}/METADATA +5 -8
- {vantage6-5.0.0a21.dist-info → vantage6-5.0.0a26.dist-info}/RECORD +39 -27
- {vantage6-5.0.0a21.dist-info → vantage6-5.0.0a26.dist-info}/WHEEL +0 -0
- {vantage6-5.0.0a21.dist-info → vantage6-5.0.0a26.dist-info}/entry_points.txt +0 -0
- {vantage6-5.0.0a21.dist-info → vantage6-5.0.0a26.dist-info}/top_level.txt +0 -0
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from pathlib import Path
|
|
3
|
+
from typing import Any
|
|
3
4
|
|
|
4
5
|
import questionary as q
|
|
5
6
|
|
|
6
|
-
from vantage6.common import
|
|
7
|
+
from vantage6.common import error, info, warning
|
|
8
|
+
from vantage6.common.client.node_client import NodeClient
|
|
9
|
+
from vantage6.common.context import AppContext
|
|
7
10
|
from vantage6.common.globals import (
|
|
8
11
|
DATABASE_TYPES,
|
|
12
|
+
DEFAULT_API_PATH,
|
|
9
13
|
InstanceType,
|
|
10
14
|
NodePolicy,
|
|
11
15
|
Ports,
|
|
12
|
-
DEFAULT_API_PATH,
|
|
13
16
|
RequiredNodeEnvVars,
|
|
14
17
|
)
|
|
15
|
-
|
|
16
|
-
from vantage6.
|
|
17
|
-
from vantage6.common import error, warning, info
|
|
18
|
-
from vantage6.cli.context import select_context_class
|
|
18
|
+
|
|
19
|
+
from vantage6.cli.config import CliConfig
|
|
19
20
|
from vantage6.cli.configuration_manager import (
|
|
20
21
|
NodeConfigurationManager,
|
|
21
22
|
ServerConfigurationManager,
|
|
22
23
|
)
|
|
24
|
+
from vantage6.cli.context import select_context_class
|
|
23
25
|
|
|
24
26
|
|
|
25
27
|
def node_configuration_questionaire(dirs: dict, instance_name: str) -> dict:
|
|
@@ -368,115 +370,69 @@ def _get_common_server_config(instance_type: InstanceType, instance_name: str) -
|
|
|
368
370
|
return config
|
|
369
371
|
|
|
370
372
|
|
|
371
|
-
def server_configuration_questionaire(
|
|
373
|
+
def server_configuration_questionaire(
|
|
374
|
+
instance_name: str,
|
|
375
|
+
) -> dict[str, Any]:
|
|
372
376
|
"""
|
|
373
|
-
|
|
377
|
+
Kubernetes-specific questionnaire to generate Helm values for server.
|
|
374
378
|
|
|
375
379
|
Parameters
|
|
376
380
|
----------
|
|
377
381
|
instance_name : str
|
|
378
|
-
Name of the server instance
|
|
382
|
+
Name of the server instance
|
|
379
383
|
|
|
380
384
|
Returns
|
|
381
385
|
-------
|
|
382
|
-
dict
|
|
383
|
-
|
|
386
|
+
dict[str, Any]
|
|
387
|
+
dictionary with Helm values for the server configuration
|
|
384
388
|
"""
|
|
389
|
+
# Get active kube namespace
|
|
390
|
+
cli_config = CliConfig()
|
|
391
|
+
kube_namespace = cli_config.get_last_namespace()
|
|
385
392
|
|
|
386
|
-
config
|
|
387
|
-
|
|
388
|
-
constant_jwt_secret = q.confirm("Do you want a constant JWT secret?").unsafe_ask()
|
|
389
|
-
if constant_jwt_secret:
|
|
390
|
-
config["jwt_secret_key"] = generate_apikey()
|
|
393
|
+
# Initialize config with basic structure
|
|
394
|
+
config = {"server": {}, "database": {}, "ui": {}}
|
|
391
395
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
396
|
+
# === Server settings ===
|
|
397
|
+
config["server"]["description"] = q.text(
|
|
398
|
+
"Enter a human-readable description:",
|
|
399
|
+
default=f"Vantage6 server {instance_name}",
|
|
400
|
+
).unsafe_ask()
|
|
395
401
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
"
|
|
399
|
-
" development server running locally, this is the same as the "
|
|
400
|
-
"server url. If you are running a production server, this is the "
|
|
401
|
-
"url that users will connect to.",
|
|
402
|
-
default=current_server_url,
|
|
402
|
+
config["server"]["image"] = q.text(
|
|
403
|
+
"Server Docker image:",
|
|
404
|
+
default="harbor2.vantage6.ai/infrastructure/server:latest",
|
|
403
405
|
).unsafe_ask()
|
|
404
406
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
+
# === UI settings ===
|
|
408
|
+
config["ui"]["image"] = q.text(
|
|
409
|
+
"UI Docker image:",
|
|
410
|
+
default="harbor2.vantage6.ai/infrastructure/ui:latest",
|
|
407
411
|
).unsafe_ask()
|
|
408
|
-
if is_add_vpn:
|
|
409
|
-
vpn_config = q.unsafe_prompt(
|
|
410
|
-
[
|
|
411
|
-
{
|
|
412
|
-
"type": "text",
|
|
413
|
-
"name": "url",
|
|
414
|
-
"message": "VPN server URL:",
|
|
415
|
-
},
|
|
416
|
-
{
|
|
417
|
-
"type": "text",
|
|
418
|
-
"name": "portal_username",
|
|
419
|
-
"message": "VPN portal username:",
|
|
420
|
-
},
|
|
421
|
-
{
|
|
422
|
-
"type": "password",
|
|
423
|
-
"name": "portal_userpass",
|
|
424
|
-
"message": "VPN portal password:",
|
|
425
|
-
},
|
|
426
|
-
{
|
|
427
|
-
"type": "text",
|
|
428
|
-
"name": "client_id",
|
|
429
|
-
"message": "VPN client username:",
|
|
430
|
-
},
|
|
431
|
-
{
|
|
432
|
-
"type": "password",
|
|
433
|
-
"name": "client_secret",
|
|
434
|
-
"message": "VPN client password:",
|
|
435
|
-
},
|
|
436
|
-
{
|
|
437
|
-
"type": "text",
|
|
438
|
-
"name": "redirect_url",
|
|
439
|
-
"message": "Redirect url (should be local address of server)",
|
|
440
|
-
"default": "http://localhost",
|
|
441
|
-
},
|
|
442
|
-
]
|
|
443
|
-
)
|
|
444
|
-
config["vpn_server"] = vpn_config
|
|
445
412
|
|
|
446
|
-
|
|
447
|
-
|
|
413
|
+
# === Database settings ===
|
|
414
|
+
config["database"]["volumePath"] = q.text(
|
|
415
|
+
"Where is your server database located on the host machine?",
|
|
416
|
+
default=f"{Path.cwd()}/dev/.db/db_pv_server",
|
|
448
417
|
).unsafe_ask()
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
).unsafe_ask()
|
|
454
|
-
config["rabbitmq"] = {
|
|
455
|
-
"uri": rabbit_uri,
|
|
456
|
-
"start_with_server": run_rabbit_locally,
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
# add algorithm stores to this server
|
|
460
|
-
is_add_community_store = q.confirm(
|
|
461
|
-
"Do you want to make the algorithms from the community algorithm store "
|
|
462
|
-
"available to your users?"
|
|
418
|
+
|
|
419
|
+
config["database"]["k8sNodeName"] = q.text(
|
|
420
|
+
"What is the name of the k8s node where the databases are running?",
|
|
421
|
+
default="docker-desktop",
|
|
463
422
|
).unsafe_ask()
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
423
|
+
|
|
424
|
+
# === Keycloak settings ===
|
|
425
|
+
keycloak_url = f"http://vantage6-auth-keycloak.{kube_namespace}.svc.cluster.local"
|
|
426
|
+
config["server"]["keycloakUrl"] = keycloak_url
|
|
427
|
+
|
|
428
|
+
# === Other settings ===
|
|
429
|
+
log_level = q.select(
|
|
430
|
+
"Which level of logging would you like?",
|
|
431
|
+
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
|
432
|
+
default="INFO",
|
|
471
433
|
).unsafe_ask()
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
store_url = q.text(message="Enter the URL of the store:").unsafe_ask()
|
|
475
|
-
algorithm_stores.append({"name": store_name, "url": store_url})
|
|
476
|
-
add_more_stores = q.confirm(
|
|
477
|
-
"Do you want to add more algorithm stores?", default=False
|
|
478
|
-
).unsafe_ask()
|
|
479
|
-
config["algorithm_stores"] = algorithm_stores
|
|
434
|
+
|
|
435
|
+
config["server"]["logging"] = {"level": log_level}
|
|
480
436
|
|
|
481
437
|
return config
|
|
482
438
|
|
|
@@ -520,7 +476,6 @@ def algo_store_configuration_questionaire(instance_name: str) -> dict:
|
|
|
520
476
|
}
|
|
521
477
|
|
|
522
478
|
# ask about openness of the algorithm store
|
|
523
|
-
config["policies"] = {"allow_localhost": False}
|
|
524
479
|
is_open = q.confirm(
|
|
525
480
|
"Do you want to open the algorithm store to the public? This will allow anyone "
|
|
526
481
|
"to view the algorithms, but they cannot modify them.",
|
|
@@ -530,19 +485,21 @@ def algo_store_configuration_questionaire(instance_name: str) -> dict:
|
|
|
530
485
|
open_algos_policy = "public"
|
|
531
486
|
else:
|
|
532
487
|
is_open_to_whitelist = q.confirm(
|
|
533
|
-
"Do you want to allow all users
|
|
488
|
+
"Do you want to allow all authenticated users to access "
|
|
534
489
|
"the algorithms in the store? If not allowing this, you will have to assign"
|
|
535
490
|
" the appropriate permissions to each user individually.",
|
|
536
491
|
default=True,
|
|
537
492
|
).unsafe_ask()
|
|
538
|
-
open_algos_policy = "
|
|
493
|
+
open_algos_policy = "authenticated" if is_open_to_whitelist else "private"
|
|
539
494
|
config["policies"]["algorithm_view"] = open_algos_policy
|
|
540
495
|
|
|
541
496
|
return config
|
|
542
497
|
|
|
543
498
|
|
|
544
499
|
def configuration_wizard(
|
|
545
|
-
type_: InstanceType,
|
|
500
|
+
type_: InstanceType,
|
|
501
|
+
instance_name: str,
|
|
502
|
+
system_folders: bool,
|
|
546
503
|
) -> Path:
|
|
547
504
|
"""
|
|
548
505
|
Create a configuration file for a node or server instance.
|
|
@@ -570,7 +527,9 @@ def configuration_wizard(
|
|
|
570
527
|
config = node_configuration_questionaire(dirs, instance_name)
|
|
571
528
|
elif type_ == InstanceType.SERVER:
|
|
572
529
|
conf_manager = ServerConfigurationManager
|
|
573
|
-
config = server_configuration_questionaire(
|
|
530
|
+
config = server_configuration_questionaire(
|
|
531
|
+
instance_name=instance_name,
|
|
532
|
+
)
|
|
574
533
|
else:
|
|
575
534
|
conf_manager = ServerConfigurationManager
|
|
576
535
|
config = algo_store_configuration_questionaire(instance_name)
|
vantage6/cli/context/node.py
CHANGED
|
@@ -16,9 +16,6 @@ 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
|
|
@@ -188,48 +185,6 @@ class NodeContext(AppContext):
|
|
|
188
185
|
"""
|
|
189
186
|
return os.environ.get("DATA_VOLUME_NAME", f"{self.docker_container_name}-vol")
|
|
190
187
|
|
|
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
188
|
@property
|
|
234
189
|
def proxy_log_file(self):
|
|
235
190
|
return self.log_file_name(type_="proxy_server")
|
|
@@ -250,6 +205,40 @@ class NodeContext(AppContext):
|
|
|
250
205
|
"""
|
|
251
206
|
return self.config["databases"][label]
|
|
252
207
|
|
|
208
|
+
def set_folders(
|
|
209
|
+
self, instance_type: InstanceType, instance_name: str, system_folders: bool
|
|
210
|
+
) -> None:
|
|
211
|
+
"""
|
|
212
|
+
Set the folders for the node.
|
|
213
|
+
|
|
214
|
+
Parameters
|
|
215
|
+
----------
|
|
216
|
+
instance_type : InstanceType
|
|
217
|
+
Instance type
|
|
218
|
+
instance_name : str
|
|
219
|
+
Instance name
|
|
220
|
+
system_folders : bool
|
|
221
|
+
Whether to use system folders
|
|
222
|
+
"""
|
|
223
|
+
dirs = self.instance_folders(instance_type, instance_name, system_folders)
|
|
224
|
+
|
|
225
|
+
self.log_dir = dirs.get("log")
|
|
226
|
+
self.data_dir = dirs.get("data")
|
|
227
|
+
self.config_dir = dirs.get("config")
|
|
228
|
+
|
|
229
|
+
@staticmethod
|
|
230
|
+
def instance_folders(*_args, **_kwargs) -> dict:
|
|
231
|
+
"""Log, data and config folders are always mounted. The node manager
|
|
232
|
+
should take care of this.
|
|
233
|
+
"""
|
|
234
|
+
mnt = Path("/mnt")
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
"log": mnt / "log",
|
|
238
|
+
"data": mnt / "data",
|
|
239
|
+
"config": mnt / "config",
|
|
240
|
+
}
|
|
241
|
+
|
|
253
242
|
def __create_node_identifier(self) -> str:
|
|
254
243
|
"""
|
|
255
244
|
Create a unique identifier for the node.
|
vantage6/cli/context/server.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
from pathlib import Path
|
|
2
3
|
|
|
3
4
|
from vantage6.common.globals import APPNAME, InstanceType
|
|
4
5
|
from vantage6.cli.configuration_manager import ServerConfigurationManager
|
|
5
6
|
from vantage6.cli.globals import (
|
|
6
7
|
DEFAULT_SERVER_SYSTEM_FOLDERS as S_FOL,
|
|
8
|
+
PROMETHEUS_DIR,
|
|
7
9
|
ServerType,
|
|
8
10
|
ServerGlobals,
|
|
9
11
|
)
|
|
@@ -57,6 +59,30 @@ class ServerContext(BaseServerContext):
|
|
|
57
59
|
"""
|
|
58
60
|
return f"{APPNAME}-{self.name}-{self.scope}-{ServerType.V6SERVER}"
|
|
59
61
|
|
|
62
|
+
@property
|
|
63
|
+
def prometheus_container_name(self) -> str:
|
|
64
|
+
"""
|
|
65
|
+
Get the name of the Prometheus Docker container for this server.
|
|
66
|
+
|
|
67
|
+
Returns
|
|
68
|
+
-------
|
|
69
|
+
str
|
|
70
|
+
Prometheus container name, unique to this server instance.
|
|
71
|
+
"""
|
|
72
|
+
return f"{APPNAME}-{self.name}-{self.scope}-prometheus"
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def prometheus_dir(self) -> Path:
|
|
76
|
+
"""
|
|
77
|
+
Get the Prometheus directory path.
|
|
78
|
+
|
|
79
|
+
Returns
|
|
80
|
+
-------
|
|
81
|
+
Path
|
|
82
|
+
Path to the Prometheus directory
|
|
83
|
+
"""
|
|
84
|
+
return self.data_dir / PROMETHEUS_DIR
|
|
85
|
+
|
|
60
86
|
@classmethod
|
|
61
87
|
def from_external_config_file(
|
|
62
88
|
cls, path: str, system_folders: bool = S_FOL
|
vantage6/cli/dev/create.py
CHANGED
|
@@ -12,7 +12,7 @@ from vantage6.common import ensure_config_dir_writable, info, error, generate_ap
|
|
|
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
|
|
15
|
+
from vantage6.cli.globals import PACKAGE_FOLDER, DefaultDatasets
|
|
16
16
|
from vantage6.cli.context.server import ServerContext
|
|
17
17
|
from vantage6.cli.context.node import NodeContext
|
|
18
18
|
from vantage6.cli.server.common import get_server_context
|
|
@@ -20,7 +20,9 @@ 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(
|
|
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(
|
|
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,
|
|
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
|
-
|
|
81
|
-
|
|
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":
|
|
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
|
-
|
|
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,
|
|
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
|
|
|
@@ -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,
|
|
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(
|
|
@@ -548,6 +599,14 @@ def demo_network(
|
|
|
548
599
|
help="YAML File with additional algorithm store configuration. This will be"
|
|
549
600
|
" appended to the algorithm store configuration file",
|
|
550
601
|
)
|
|
602
|
+
@click.option(
|
|
603
|
+
"--add-dataset",
|
|
604
|
+
type=(str, click.Path()),
|
|
605
|
+
default=(),
|
|
606
|
+
multiple=True,
|
|
607
|
+
help="Add a dataset to the nodes. The first argument is the label of the database, "
|
|
608
|
+
"the second is the path to the dataset file.",
|
|
609
|
+
)
|
|
551
610
|
@click.pass_context
|
|
552
611
|
def create_demo_network(
|
|
553
612
|
click_ctx: click.Context,
|
|
@@ -562,6 +621,7 @@ def create_demo_network(
|
|
|
562
621
|
extra_server_config: Path = None,
|
|
563
622
|
extra_node_config: Path = None,
|
|
564
623
|
extra_store_config: Path = None,
|
|
624
|
+
add_dataset: list[tuple[str, Path]] = (),
|
|
565
625
|
) -> dict:
|
|
566
626
|
"""Creates a demo network.
|
|
567
627
|
|
|
@@ -583,6 +643,7 @@ def create_demo_network(
|
|
|
583
643
|
ui_image,
|
|
584
644
|
ui_port,
|
|
585
645
|
algorithm_store_port,
|
|
646
|
+
list(add_dataset),
|
|
586
647
|
)
|
|
587
648
|
info(
|
|
588
649
|
f"Created {Fore.GREEN}{len(demo[0])}{Style.RESET_ALL} node "
|