vantage6 4.10.2__py3-none-any.whl → 4.11.0rc3__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/_version.py +1 -1
- vantage6/cli/cli.py +2 -0
- vantage6/cli/configuration_manager.py +1 -0
- 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/globals.py +12 -0
- vantage6/cli/prometheus/monitoring_manager.py +145 -0
- vantage6/cli/prometheus/prometheus.yml +5 -0
- vantage6/cli/server/start.py +58 -2
- vantage6/cli/server/stop.py +7 -0
- vantage6/cli/template/node_config.j2 +4 -2
- vantage6/cli/test/algo_test_scripts/algo_test_arguments.py +33 -0
- vantage6/cli/test/algo_test_scripts/algo_test_script.py +89 -0
- vantage6/cli/test/client_script.py +147 -0
- {vantage6-4.10.2.dist-info → vantage6-4.11.0rc3.dist-info}/METADATA +3 -3
- {vantage6-4.10.2.dist-info → vantage6-4.11.0rc3.dist-info}/RECORD +22 -15
- {vantage6-4.10.2.dist-info → vantage6-4.11.0rc3.dist-info}/WHEEL +0 -0
- {vantage6-4.10.2.dist-info → vantage6-4.11.0rc3.dist-info}/entry_points.txt +0 -0
- {vantage6-4.10.2.dist-info → vantage6-4.11.0rc3.dist-info}/top_level.txt +0 -0
vantage6/cli/globals.py
CHANGED
|
@@ -42,6 +42,18 @@ DIAGNOSTICS_IMAGE = "harbor2.vantage6.ai/algorithms/diagnostic"
|
|
|
42
42
|
# Address of community algorithm store
|
|
43
43
|
COMMUNITY_STORE = "https://store.cotopaxi.vantage6.ai/api"
|
|
44
44
|
|
|
45
|
+
DEFAULT_PROMETHEUS_IMAGE = "prom/prometheus"
|
|
46
|
+
PROMETHEUS_CONFIG = "prometheus.yml"
|
|
47
|
+
PROMETHEUS_DIR = "prometheus"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# datasets included in the nodes of the dev network
|
|
51
|
+
class DefaultDatasets(str, Enum):
|
|
52
|
+
"""Enum containing default datasets"""
|
|
53
|
+
|
|
54
|
+
OLYMPIC_ATHLETES = "olympic_athletes_2016.csv"
|
|
55
|
+
KAPLAN_MEIER_TEST = "km_dataset.csv"
|
|
56
|
+
|
|
45
57
|
|
|
46
58
|
class ServerType(str, Enum):
|
|
47
59
|
"""Enum containing server types"""
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import yaml
|
|
2
|
+
import docker
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from vantage6.common import info, error
|
|
6
|
+
from vantage6.common.docker.network_manager import NetworkManager
|
|
7
|
+
from vantage6.common.globals import DEFAULT_PROMETHEUS_EXPORTER_PORT
|
|
8
|
+
from vantage6.cli.context.server import ServerContext
|
|
9
|
+
from vantage6.cli.globals import (
|
|
10
|
+
DEFAULT_PROMETHEUS_IMAGE,
|
|
11
|
+
PROMETHEUS_CONFIG,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PrometheusServer:
|
|
16
|
+
"""
|
|
17
|
+
Manages the Prometheus Docker container
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self, ctx: ServerContext, network_mgr: NetworkManager, image: str = None
|
|
22
|
+
):
|
|
23
|
+
"""
|
|
24
|
+
Initialize the PrometheusServer instance.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
ctx : ServerContext
|
|
29
|
+
The server context containing configuration and paths.
|
|
30
|
+
network_mgr : NetworkManager
|
|
31
|
+
The network manager responsible for managing Docker networks.
|
|
32
|
+
image : str, optional
|
|
33
|
+
The Docker image to use for the Prometheus container. If not provided,
|
|
34
|
+
the default Prometheus image will be used.
|
|
35
|
+
"""
|
|
36
|
+
self.ctx = ctx
|
|
37
|
+
self.network_mgr = network_mgr
|
|
38
|
+
self.docker = docker.from_env()
|
|
39
|
+
self.image = image if image else DEFAULT_PROMETHEUS_IMAGE
|
|
40
|
+
self.config_file = Path(self.ctx.data_dir / PROMETHEUS_CONFIG)
|
|
41
|
+
self.data_dir = self.ctx.prometheus_dir
|
|
42
|
+
|
|
43
|
+
def start(self):
|
|
44
|
+
"""
|
|
45
|
+
Start a Docker container running Prometheus
|
|
46
|
+
"""
|
|
47
|
+
self._prepare_config()
|
|
48
|
+
|
|
49
|
+
volumes = {
|
|
50
|
+
str(self.config_file): {
|
|
51
|
+
"bind": "/etc/prometheus/prometheus.yml",
|
|
52
|
+
"mode": "ro",
|
|
53
|
+
},
|
|
54
|
+
str(self.data_dir): {"bind": "/prometheus", "mode": "rw"},
|
|
55
|
+
}
|
|
56
|
+
ports = {"9090/tcp": 9090}
|
|
57
|
+
|
|
58
|
+
if self._is_container_running():
|
|
59
|
+
info("Prometheus is already running!")
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
self.docker.containers.run(
|
|
63
|
+
name=self.ctx.prometheus_container_name,
|
|
64
|
+
image=self.image,
|
|
65
|
+
volumes=volumes,
|
|
66
|
+
ports=ports,
|
|
67
|
+
detach=True,
|
|
68
|
+
restart_policy={"Name": "unless-stopped"},
|
|
69
|
+
network=self.network_mgr.network_name,
|
|
70
|
+
)
|
|
71
|
+
info("Prometheus container started successfully!")
|
|
72
|
+
|
|
73
|
+
def _prepare_config(self):
|
|
74
|
+
"""
|
|
75
|
+
Prepare the Prometheus configuration and data directories
|
|
76
|
+
"""
|
|
77
|
+
if not self.config_file.exists():
|
|
78
|
+
error(f"Prometheus configuration file {self.config_file} not found!")
|
|
79
|
+
raise FileNotFoundError(f"{self.config_file} not found!")
|
|
80
|
+
|
|
81
|
+
if not self.data_dir.exists():
|
|
82
|
+
self.data_dir.mkdir(parents=True, exist_ok=True)
|
|
83
|
+
|
|
84
|
+
self._update_prometheus_config()
|
|
85
|
+
|
|
86
|
+
def _update_prometheus_config(self):
|
|
87
|
+
"""
|
|
88
|
+
Update the Prometheus configuration file with the server address.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
prometheus_exporter_port = self.ctx.config.get("prometheus", {}).get(
|
|
93
|
+
"exporter_port", DEFAULT_PROMETHEUS_EXPORTER_PORT
|
|
94
|
+
)
|
|
95
|
+
server_hostname = self.ctx.prometheus_container_name
|
|
96
|
+
server_address = f"{server_hostname}:{prometheus_exporter_port}"
|
|
97
|
+
|
|
98
|
+
info(
|
|
99
|
+
f"Using Docker container hostname '{server_hostname}' for Prometheus target. "
|
|
100
|
+
"Ensure Prometheus is in the same Docker network to resolve this address."
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
with open(self.config_file, "r") as f:
|
|
104
|
+
config = yaml.safe_load(f)
|
|
105
|
+
|
|
106
|
+
job_name = "vantage6_server_metrics"
|
|
107
|
+
job_exists = any(
|
|
108
|
+
job.get("job_name") == job_name
|
|
109
|
+
for job in config.get("scrape_configs", [])
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
if not job_exists:
|
|
113
|
+
new_job = {
|
|
114
|
+
"job_name": job_name,
|
|
115
|
+
"static_configs": [{"targets": [server_address]}],
|
|
116
|
+
}
|
|
117
|
+
config.setdefault("scrape_configs", []).append(new_job)
|
|
118
|
+
else:
|
|
119
|
+
for job in config["scrape_configs"]:
|
|
120
|
+
if job.get("job_name") == job_name:
|
|
121
|
+
job["static_configs"] = [{"targets": [server_address]}]
|
|
122
|
+
|
|
123
|
+
with open(self.config_file, "w") as f:
|
|
124
|
+
yaml.dump(config, f)
|
|
125
|
+
|
|
126
|
+
info(f"Prometheus configuration updated with target: {server_address}")
|
|
127
|
+
|
|
128
|
+
except Exception as e:
|
|
129
|
+
error(f"Failed to update Prometheus configuration: {e}")
|
|
130
|
+
raise
|
|
131
|
+
|
|
132
|
+
def _is_container_running(self) -> bool:
|
|
133
|
+
"""
|
|
134
|
+
Check if a Prometheus container is already running.
|
|
135
|
+
|
|
136
|
+
Returns
|
|
137
|
+
-------
|
|
138
|
+
bool
|
|
139
|
+
True if the Prometheus container is running, False otherwise.
|
|
140
|
+
"""
|
|
141
|
+
try:
|
|
142
|
+
container = self.docker.containers.get(self.ctx.prometheus_container_name)
|
|
143
|
+
return container.status == "running"
|
|
144
|
+
except docker.errors.NotFound:
|
|
145
|
+
return False
|
vantage6/cli/server/start.py
CHANGED
|
@@ -11,11 +11,12 @@ from vantage6.common.globals import (
|
|
|
11
11
|
InstanceType,
|
|
12
12
|
)
|
|
13
13
|
|
|
14
|
-
from vantage6.common.globals import Ports
|
|
14
|
+
from vantage6.common.globals import Ports, DEFAULT_PROMETHEUS_EXPORTER_PORT
|
|
15
15
|
from vantage6.cli.context.server import ServerContext
|
|
16
16
|
from vantage6.cli.rabbitmq.queue_manager import RabbitMQManager
|
|
17
17
|
from vantage6.cli.server.common import stop_ui
|
|
18
18
|
from vantage6.cli.common.decorator import click_insert_context
|
|
19
|
+
from vantage6.cli.prometheus.monitoring_manager import PrometheusServer
|
|
19
20
|
from vantage6.cli.common.start import (
|
|
20
21
|
attach_logs,
|
|
21
22
|
check_for_start,
|
|
@@ -49,6 +50,14 @@ from vantage6.cli.common.start import (
|
|
|
49
50
|
"container - use in development only",
|
|
50
51
|
)
|
|
51
52
|
@click.option("--rabbitmq-image", default=None, help="RabbitMQ docker image to use")
|
|
53
|
+
@click.option(
|
|
54
|
+
"--with-prometheus",
|
|
55
|
+
"start_prometheus",
|
|
56
|
+
flag_value=True,
|
|
57
|
+
default=False,
|
|
58
|
+
help="Start Prometheus monitoring as a local container",
|
|
59
|
+
)
|
|
60
|
+
@click.option("--prometheus-image", default=None, help="Prometheus docker image to use")
|
|
52
61
|
@click.option(
|
|
53
62
|
"--keep/--auto-remove",
|
|
54
63
|
default=False,
|
|
@@ -75,6 +84,8 @@ def cli_server_start(
|
|
|
75
84
|
ui_port: int,
|
|
76
85
|
start_rabbitmq: bool,
|
|
77
86
|
rabbitmq_image: str,
|
|
87
|
+
start_prometheus: bool,
|
|
88
|
+
prometheus_image: str,
|
|
78
89
|
keep: bool,
|
|
79
90
|
mount_src: str,
|
|
80
91
|
attach: bool,
|
|
@@ -135,6 +146,24 @@ def cli_server_start(
|
|
|
135
146
|
"cannot be scaled horizontally!"
|
|
136
147
|
)
|
|
137
148
|
|
|
149
|
+
if (
|
|
150
|
+
start_prometheus
|
|
151
|
+
or ctx.config.get("prometheus")
|
|
152
|
+
and ctx.config["prometheus"].get("start_with_server", False)
|
|
153
|
+
):
|
|
154
|
+
info("Starting Prometheus container")
|
|
155
|
+
_start_prometheus(ctx, prometheus_image, server_network_mgr)
|
|
156
|
+
elif ctx.config.get("prometheus"):
|
|
157
|
+
info(
|
|
158
|
+
"Prometheus is provided in the config file as external service. "
|
|
159
|
+
"Assuming this service is up and running."
|
|
160
|
+
)
|
|
161
|
+
else:
|
|
162
|
+
warning(
|
|
163
|
+
"Monitoring is not set up! This means that the vantage6 server "
|
|
164
|
+
"cannot be monitored with Prometheus!"
|
|
165
|
+
)
|
|
166
|
+
|
|
138
167
|
# start the UI if requested
|
|
139
168
|
if start_ui or ctx.config.get("ui") and ctx.config["ui"].get("enabled"):
|
|
140
169
|
_start_ui(docker_client, ctx, ui_port)
|
|
@@ -152,6 +181,9 @@ def cli_server_start(
|
|
|
152
181
|
|
|
153
182
|
info("Run Docker container")
|
|
154
183
|
port_ = str(port or ctx.config["port"] or Ports.DEV_SERVER.value)
|
|
184
|
+
prometheus_exporter_port = ctx.config.get("prometheus", {}).get(
|
|
185
|
+
"exporter_port", DEFAULT_PROMETHEUS_EXPORTER_PORT
|
|
186
|
+
)
|
|
155
187
|
container = docker_client.containers.run(
|
|
156
188
|
image,
|
|
157
189
|
command=cmd,
|
|
@@ -162,7 +194,10 @@ def cli_server_start(
|
|
|
162
194
|
"name": ctx.config_file_name,
|
|
163
195
|
},
|
|
164
196
|
environment=environment_vars,
|
|
165
|
-
ports={
|
|
197
|
+
ports={
|
|
198
|
+
f"{internal_port}/tcp": (ip, port_), # API port
|
|
199
|
+
f"{prometheus_exporter_port}/tcp": prometheus_exporter_port,
|
|
200
|
+
},
|
|
166
201
|
name=ctx.docker_container_name,
|
|
167
202
|
auto_remove=not keep,
|
|
168
203
|
tty=True,
|
|
@@ -202,6 +237,27 @@ def _start_rabbitmq(
|
|
|
202
237
|
rabbit_mgr.start()
|
|
203
238
|
|
|
204
239
|
|
|
240
|
+
def _start_prometheus(
|
|
241
|
+
ctx: ServerContext, prometheus_image: str, network_mgr: NetworkManager
|
|
242
|
+
) -> None:
|
|
243
|
+
"""
|
|
244
|
+
Start the Prometheus container if it is not already running.
|
|
245
|
+
|
|
246
|
+
Parameters
|
|
247
|
+
----------
|
|
248
|
+
ctx : ServerContext
|
|
249
|
+
Server context object
|
|
250
|
+
prometheus_image : str
|
|
251
|
+
Prometheus image to use
|
|
252
|
+
network_mgr : NetworkManager
|
|
253
|
+
Network manager object
|
|
254
|
+
"""
|
|
255
|
+
prometheus_server = PrometheusServer(
|
|
256
|
+
ctx=ctx, network_mgr=network_mgr, image=prometheus_image
|
|
257
|
+
)
|
|
258
|
+
prometheus_server.start()
|
|
259
|
+
|
|
260
|
+
|
|
205
261
|
def _start_ui(client: DockerClient, ctx: ServerContext, ui_port: int) -> None:
|
|
206
262
|
"""
|
|
207
263
|
Start the UI container.
|
vantage6/cli/server/stop.py
CHANGED
|
@@ -117,3 +117,10 @@ def _stop_server_containers(
|
|
|
117
117
|
f"Stopped the {Fore.GREEN}{rabbit_container_name}"
|
|
118
118
|
f"{Style.RESET_ALL} container."
|
|
119
119
|
)
|
|
120
|
+
|
|
121
|
+
if ctx.config.get("prometheus", {}).get("enabled"):
|
|
122
|
+
remove_container_if_exists(client, name=ctx.prometheus_container_name)
|
|
123
|
+
info(
|
|
124
|
+
f"Stopped the {Fore.GREEN}{ctx.prometheus_container_name}"
|
|
125
|
+
f"{Style.RESET_ALL} container."
|
|
126
|
+
)
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
api_key: {{ api_key }}
|
|
2
2
|
api_path: /api
|
|
3
3
|
databases:
|
|
4
|
-
{% for
|
|
4
|
+
{% for db in databases %}
|
|
5
|
+
{% for label, path in db.items() %}
|
|
5
6
|
- label: {{ label }}
|
|
6
7
|
uri: {{ path }}
|
|
7
8
|
type: csv
|
|
8
9
|
{% endfor %}
|
|
10
|
+
{% endfor %}
|
|
9
11
|
encryption:
|
|
10
12
|
enabled: false
|
|
11
13
|
private_key: null
|
|
@@ -31,4 +33,4 @@ logging:
|
|
|
31
33
|
port: {{ port }}
|
|
32
34
|
server_url: {{ server_url }}
|
|
33
35
|
task_dir: {{ task_dir}}
|
|
34
|
-
{{ user_provided_config }}
|
|
36
|
+
{{- user_provided_config -}}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
average = {
|
|
2
|
+
"collaboration": 1,
|
|
3
|
+
"organizations": [1],
|
|
4
|
+
"name": "test_average_task",
|
|
5
|
+
"image": "harbor2.vantage6.ai/demo/average",
|
|
6
|
+
"description": "",
|
|
7
|
+
"input_": {
|
|
8
|
+
"method": "central_average",
|
|
9
|
+
"args": [],
|
|
10
|
+
"kwargs": {"column_name": "Age"},
|
|
11
|
+
},
|
|
12
|
+
"databases": [{"label": "olympic_athletes"}],
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
kaplan_meier = {
|
|
16
|
+
"collaboration": 1,
|
|
17
|
+
"organizations": [1],
|
|
18
|
+
"name": "test_average_task",
|
|
19
|
+
"image": "harbor2.vantage6.ai/algorithms/kaplan-meier",
|
|
20
|
+
"description": "",
|
|
21
|
+
"input_": {
|
|
22
|
+
"method": "kaplan_meier_central",
|
|
23
|
+
"args": [],
|
|
24
|
+
"kwargs": {
|
|
25
|
+
"time_column_name": "days",
|
|
26
|
+
"censor_column_name": "censor",
|
|
27
|
+
"organizations_to_include": [1, 2, 3],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
"databases": [{"label": "kaplan_meier_test"}],
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
args = {"average": average, "kaplan_meier": kaplan_meier}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import algo_test_arguments as arguments
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
import vantage6.common.task_status as task_status
|
|
7
|
+
|
|
8
|
+
from vantage6.client import Client
|
|
9
|
+
from vantage6.common import error
|
|
10
|
+
from vantage6.common.globals import Ports
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def create_and_run_task(client: Client, task_args: dict, algo_name: str = "algorithm"):
|
|
14
|
+
"""
|
|
15
|
+
Create and run a task using the provided client and task arguments.
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
client: Client
|
|
20
|
+
The client instance to use for creating and running the task.
|
|
21
|
+
task_args: dict
|
|
22
|
+
The arguments to pass to the task creation method.
|
|
23
|
+
algo_name: str, optional
|
|
24
|
+
The name of the algorithm for logging purposes. Default is "algorithm".
|
|
25
|
+
|
|
26
|
+
Raises
|
|
27
|
+
------
|
|
28
|
+
AssertionError: If the task fails.
|
|
29
|
+
"""
|
|
30
|
+
task = client.task.create(**task_args)
|
|
31
|
+
task_id = task["id"]
|
|
32
|
+
client.wait_for_results(task_id)
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
# check if the task has failed
|
|
36
|
+
assert not task_status.has_task_failed(client.task.get(task_id)["status"])
|
|
37
|
+
|
|
38
|
+
logging.info(f"Task for {algo_name} completed successfully.")
|
|
39
|
+
|
|
40
|
+
except AssertionError:
|
|
41
|
+
error(
|
|
42
|
+
f"Task for {algo_name} failed. Check the log file of the task "
|
|
43
|
+
f"{task_id} for more information."
|
|
44
|
+
)
|
|
45
|
+
exit(1)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def run_test(custom_args: dict | None = None):
|
|
49
|
+
"""
|
|
50
|
+
Run a test by creating and running tasks using the provided arguments.
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
custom_args: dict, optional
|
|
55
|
+
The arguments to pass to the task creation method. If not provided,
|
|
56
|
+
the arguments from the `arguments` module will be used.
|
|
57
|
+
"""
|
|
58
|
+
# Create a client and authenticate
|
|
59
|
+
client = Client("http://localhost", Ports.DEV_SERVER.value, "/api")
|
|
60
|
+
try:
|
|
61
|
+
client.authenticate("dev_admin", "password")
|
|
62
|
+
except ConnectionError:
|
|
63
|
+
error(
|
|
64
|
+
"Could not connect to the server. Please check if a dev network is running."
|
|
65
|
+
)
|
|
66
|
+
exit(1)
|
|
67
|
+
|
|
68
|
+
# if custom arguments are provided, use them for running the task
|
|
69
|
+
if custom_args:
|
|
70
|
+
create_and_run_task(client, custom_args)
|
|
71
|
+
|
|
72
|
+
else:
|
|
73
|
+
# Run the task for each algorithm in the arguments file
|
|
74
|
+
for algo in arguments.args:
|
|
75
|
+
logging.info(f"Running task for {algo}")
|
|
76
|
+
|
|
77
|
+
task_args = arguments.args[algo]
|
|
78
|
+
create_and_run_task(client, task_args, algo)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
if __name__ == "__main__":
|
|
82
|
+
# check if arguments are provided
|
|
83
|
+
if len(sys.argv) > 1:
|
|
84
|
+
input_string = sys.argv[1].replace("'", '"')
|
|
85
|
+
json_input = json.loads(input_string)
|
|
86
|
+
else:
|
|
87
|
+
json_input = None
|
|
88
|
+
|
|
89
|
+
run_test(json_input)
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import json
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
|
|
10
|
+
from vantage6.cli.dev.create import create_demo_network
|
|
11
|
+
from vantage6.cli.dev.remove import remove_demo_network
|
|
12
|
+
from vantage6.cli.dev.start import start_demo_network
|
|
13
|
+
from vantage6.cli.dev.stop import stop_demo_network
|
|
14
|
+
from vantage6.common.globals import Ports
|
|
15
|
+
|
|
16
|
+
TEST_FILE_PATH = Path(__file__).parent / "algo_test_scripts" / "algo_test_script.py"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@click.command()
|
|
20
|
+
@click.option(
|
|
21
|
+
"--script",
|
|
22
|
+
type=click.Path(),
|
|
23
|
+
default=TEST_FILE_PATH,
|
|
24
|
+
help="Path of the script to test the algorithm. If a script is not provided, the default script is used.",
|
|
25
|
+
)
|
|
26
|
+
@click.option(
|
|
27
|
+
"--task-arguments",
|
|
28
|
+
type=str,
|
|
29
|
+
default=None,
|
|
30
|
+
help="Arguments to be provided to Task.create function. If --script is provided, this should not be set.",
|
|
31
|
+
)
|
|
32
|
+
@click.option(
|
|
33
|
+
"--create-dev-network",
|
|
34
|
+
is_flag=True,
|
|
35
|
+
help="Create a new dev network to run the test",
|
|
36
|
+
)
|
|
37
|
+
@click.option(
|
|
38
|
+
"--start-dev-network",
|
|
39
|
+
is_flag=True,
|
|
40
|
+
help="Start a dev network to run the test",
|
|
41
|
+
)
|
|
42
|
+
@click.option(
|
|
43
|
+
"-n", "--name", default=None, type=str, help="Name for your development setup"
|
|
44
|
+
)
|
|
45
|
+
@click.option(
|
|
46
|
+
"--server-url",
|
|
47
|
+
type=str,
|
|
48
|
+
default="http://host.docker.internal",
|
|
49
|
+
help="Server URL to point to. If you are using Docker Desktop, "
|
|
50
|
+
"the default http://host.docker.internal should not be changed.",
|
|
51
|
+
)
|
|
52
|
+
@click.option(
|
|
53
|
+
"-i", "--image", type=str, default=None, help="Server Docker image to use"
|
|
54
|
+
)
|
|
55
|
+
@click.option(
|
|
56
|
+
"--keep",
|
|
57
|
+
type=bool,
|
|
58
|
+
default=False,
|
|
59
|
+
help="Keep the dev network after finishing the test",
|
|
60
|
+
)
|
|
61
|
+
@click.option(
|
|
62
|
+
"--add-dataset",
|
|
63
|
+
type=(str, click.Path()),
|
|
64
|
+
default=[],
|
|
65
|
+
multiple=True,
|
|
66
|
+
help="Add a dataset to the nodes. The first argument is the label of the database, "
|
|
67
|
+
"the second is the path to the dataset file.",
|
|
68
|
+
)
|
|
69
|
+
@click.pass_context
|
|
70
|
+
def cli_test_client_script(
|
|
71
|
+
click_ctx: click.Context,
|
|
72
|
+
script: Path,
|
|
73
|
+
task_arguments: str,
|
|
74
|
+
name: str,
|
|
75
|
+
server_url: str,
|
|
76
|
+
create_dev_network: bool,
|
|
77
|
+
start_dev_network: bool,
|
|
78
|
+
image: str,
|
|
79
|
+
keep: bool,
|
|
80
|
+
add_dataset: list[tuple[str, Path]] = (),
|
|
81
|
+
) -> int:
|
|
82
|
+
"""
|
|
83
|
+
Run a script for testing an algorithm on a dev network.
|
|
84
|
+
The path to the script must be provided as an argument.
|
|
85
|
+
"""
|
|
86
|
+
if not (script or task_arguments):
|
|
87
|
+
raise click.UsageError("--script or --task-arguments must be set.")
|
|
88
|
+
elif script != TEST_FILE_PATH and task_arguments:
|
|
89
|
+
raise click.UsageError("--script and --task-arguments cannot be set together.")
|
|
90
|
+
|
|
91
|
+
# Check if the task_arguments is a valid JSON string
|
|
92
|
+
if task_arguments:
|
|
93
|
+
try:
|
|
94
|
+
json.loads(task_arguments.replace("'", '"'))
|
|
95
|
+
except json.JSONDecodeError:
|
|
96
|
+
raise click.UsageError("task-arguments must be a valid JSON string.")
|
|
97
|
+
|
|
98
|
+
# create the network
|
|
99
|
+
if create_dev_network:
|
|
100
|
+
click_ctx.invoke(
|
|
101
|
+
create_demo_network,
|
|
102
|
+
name=name,
|
|
103
|
+
num_nodes=3,
|
|
104
|
+
server_url=server_url,
|
|
105
|
+
server_port=Ports.DEV_SERVER.value,
|
|
106
|
+
image=image,
|
|
107
|
+
extra_server_config=None,
|
|
108
|
+
extra_node_config=None,
|
|
109
|
+
add_dataset=add_dataset,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# start the server and nodes
|
|
113
|
+
if create_dev_network or start_dev_network:
|
|
114
|
+
click_ctx.invoke(
|
|
115
|
+
start_demo_network,
|
|
116
|
+
name=name,
|
|
117
|
+
server_image=image,
|
|
118
|
+
node_image=image,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# run the test script and get the result
|
|
122
|
+
if not task_arguments:
|
|
123
|
+
subprocess_args = ["python", script]
|
|
124
|
+
else:
|
|
125
|
+
subprocess_args = ["python", script, task_arguments]
|
|
126
|
+
|
|
127
|
+
result = subprocess.run(subprocess_args, stdout=sys.stdout, stderr=sys.stderr)
|
|
128
|
+
|
|
129
|
+
# check the exit code. If the test passed, it should be 0
|
|
130
|
+
if result.returncode == 0:
|
|
131
|
+
msg = ":heavy_check_mark: [green]Test passed[/green]"
|
|
132
|
+
else:
|
|
133
|
+
msg = ":x: [red]Test failed[/red]"
|
|
134
|
+
|
|
135
|
+
console = Console()
|
|
136
|
+
console.print(msg)
|
|
137
|
+
|
|
138
|
+
# clean up the test resources. Keep the network if --keep is set, or if the network
|
|
139
|
+
# was created for this test. If the network was started for this test, stop it but
|
|
140
|
+
# do not remove it.
|
|
141
|
+
if not keep:
|
|
142
|
+
if create_dev_network or start_dev_network:
|
|
143
|
+
click_ctx.invoke(stop_demo_network, name=name)
|
|
144
|
+
if create_dev_network:
|
|
145
|
+
click_ctx.invoke(remove_demo_network, name=name)
|
|
146
|
+
|
|
147
|
+
return result.returncode
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: vantage6
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.11.0rc3
|
|
4
4
|
Summary: vantage6 command line interface
|
|
5
5
|
Home-page: https://github.com/vantage6/vantage6
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -16,8 +16,8 @@ Requires-Dist: questionary==1.10.0
|
|
|
16
16
|
Requires-Dist: rich==13.5.2
|
|
17
17
|
Requires-Dist: schema==0.7.5
|
|
18
18
|
Requires-Dist: SQLAlchemy==1.4.46
|
|
19
|
-
Requires-Dist: vantage6-common==4.
|
|
20
|
-
Requires-Dist: vantage6-client==4.
|
|
19
|
+
Requires-Dist: vantage6-common==4.11.0rc3
|
|
20
|
+
Requires-Dist: vantage6-client==4.11.0rc3
|
|
21
21
|
Provides-Extra: dev
|
|
22
22
|
Requires-Dist: coverage==6.4.4; extra == "dev"
|
|
23
23
|
Requires-Dist: black; extra == "dev"
|