vantage6 5.0.0a36__py3-none-any.whl → 5.0.0a38__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.
- vantage6/cli/algorithm/generate_algorithm_json.py +0 -1
- vantage6/cli/algostore/attach.py +28 -3
- vantage6/cli/algostore/list.py +2 -2
- vantage6/cli/algostore/new.py +3 -2
- vantage6/cli/algostore/start.py +25 -6
- vantage6/cli/algostore/stop.py +3 -0
- vantage6/cli/algostore/version.py +62 -0
- vantage6/cli/auth/attach.py +1 -1
- vantage6/cli/auth/list.py +2 -2
- vantage6/cli/auth/new.py +3 -2
- vantage6/cli/auth/remove.py +58 -0
- vantage6/cli/auth/start.py +27 -9
- vantage6/cli/auth/stop.py +3 -0
- vantage6/cli/cli.py +21 -0
- vantage6/cli/common/attach.py +114 -0
- vantage6/cli/common/decorator.py +25 -4
- vantage6/cli/common/list.py +68 -0
- vantage6/cli/common/new.py +27 -7
- vantage6/cli/common/remove.py +18 -0
- vantage6/cli/common/start.py +48 -40
- vantage6/cli/common/stop.py +16 -4
- vantage6/cli/common/utils.py +65 -74
- vantage6/cli/common/version.py +82 -0
- vantage6/cli/config.py +10 -2
- vantage6/cli/{configuration_wizard.py → configuration_create.py} +22 -14
- vantage6/cli/configuration_manager.py +70 -21
- vantage6/cli/context/__init__.py +10 -5
- vantage6/cli/context/algorithm_store.py +13 -7
- vantage6/cli/context/auth.py +23 -5
- vantage6/cli/context/node.py +25 -8
- vantage6/cli/context/server.py +18 -6
- vantage6/cli/globals.py +1 -0
- vantage6/cli/node/attach.py +27 -3
- vantage6/cli/node/common/__init__.py +26 -10
- vantage6/cli/node/common/task_cleanup.py +153 -0
- vantage6/cli/node/list.py +3 -44
- vantage6/cli/node/new.py +13 -6
- vantage6/cli/node/set_api_key.py +1 -1
- vantage6/cli/node/start.py +30 -7
- vantage6/cli/node/stop.py +151 -7
- vantage6/cli/node/version.py +96 -33
- vantage6/cli/sandbox/config/base.py +109 -0
- vantage6/cli/sandbox/config/core.py +300 -0
- vantage6/cli/sandbox/config/node.py +311 -0
- vantage6/cli/sandbox/data/km_dataset.csv +2401 -0
- vantage6/cli/sandbox/data/olympic_athletes_2016.csv +2425 -0
- vantage6/cli/sandbox/new.py +207 -0
- vantage6/cli/sandbox/populate/__init__.py +173 -0
- vantage6/cli/sandbox/populate/helpers/connect_store.py +203 -0
- vantage6/cli/sandbox/populate/helpers/delete_fixtures.py +67 -0
- vantage6/cli/sandbox/populate/helpers/load_fixtures.py +476 -0
- vantage6/cli/sandbox/populate/helpers/utils.py +35 -0
- vantage6/cli/sandbox/remove.py +155 -0
- vantage6/cli/sandbox/start.py +349 -0
- vantage6/cli/sandbox/stop.py +106 -0
- vantage6/cli/server/attach.py +28 -3
- vantage6/cli/server/common/__init__.py +5 -6
- vantage6/cli/server/import_.py +137 -119
- vantage6/cli/server/list.py +2 -2
- vantage6/cli/server/new.py +5 -3
- vantage6/cli/server/start.py +21 -4
- vantage6/cli/server/stop.py +2 -0
- vantage6/cli/server/version.py +31 -18
- vantage6/cli/template/algo_store_config.j2 +3 -0
- vantage6/cli/template/auth_config.j2 +24 -1
- vantage6/cli/template/node_config.j2 +2 -0
- vantage6/cli/template/server_config.j2 +10 -7
- vantage6/cli/use/context.py +8 -1
- vantage6/cli/use/namespace.py +10 -7
- vantage6/cli/utils.py +33 -1
- vantage6/cli/utils_kubernetes.py +270 -0
- {vantage6-5.0.0a36.dist-info → vantage6-5.0.0a38.dist-info}/METADATA +4 -4
- vantage6-5.0.0a38.dist-info/RECORD +102 -0
- vantage6/cli/rabbitmq/__init__.py +0 -0
- vantage6/cli/rabbitmq/definitions.py +0 -26
- vantage6/cli/rabbitmq/queue_manager.py +0 -220
- vantage6/cli/rabbitmq/rabbitmq.config +0 -8
- vantage6-5.0.0a36.dist-info/RECORD +0 -86
- {vantage6-5.0.0a36.dist-info → vantage6-5.0.0a38.dist-info}/WHEEL +0 -0
- {vantage6-5.0.0a36.dist-info → vantage6-5.0.0a38.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from vantage6.common.globals import HTTP_LOCALHOST, InstanceType, Ports
|
|
4
|
+
|
|
5
|
+
from vantage6.cli.common.new import new
|
|
6
|
+
from vantage6.cli.sandbox.config.base import BaseSandboxConfigManager
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CoreSandboxConfigManager(BaseSandboxConfigManager):
|
|
10
|
+
"""
|
|
11
|
+
Class to store the sandbox configurations.
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
server_name : str
|
|
16
|
+
Name of the server.
|
|
17
|
+
server_port : int
|
|
18
|
+
Port of the server.
|
|
19
|
+
ui_port : int
|
|
20
|
+
Port of the UI.
|
|
21
|
+
algorithm_store_port : int
|
|
22
|
+
Port of the algorithm store.
|
|
23
|
+
server_image : str | None
|
|
24
|
+
Image of the server.
|
|
25
|
+
store_image : str | None
|
|
26
|
+
Image of the algorithm store.
|
|
27
|
+
ui_image : str | None
|
|
28
|
+
Image of the UI.
|
|
29
|
+
extra_server_config : Path | None
|
|
30
|
+
Path to the extra server configuration file.
|
|
31
|
+
extra_store_config : Path | None
|
|
32
|
+
Path to the extra algorithm store configuration file.
|
|
33
|
+
context : str | None
|
|
34
|
+
Kubernetes context.
|
|
35
|
+
namespace : str | None
|
|
36
|
+
Kubernetes namespace.
|
|
37
|
+
k8s_node_name : str
|
|
38
|
+
Kubernetes node name.
|
|
39
|
+
custom_data_dir : Path | None
|
|
40
|
+
Path to the custom data directory. Useful on WSL because of mount issues for
|
|
41
|
+
default directories.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
server_name: str,
|
|
47
|
+
server_port: int,
|
|
48
|
+
ui_port: int,
|
|
49
|
+
algorithm_store_port: int,
|
|
50
|
+
server_image: str | None,
|
|
51
|
+
store_image: str | None,
|
|
52
|
+
ui_image: str | None,
|
|
53
|
+
extra_server_config: Path | None,
|
|
54
|
+
extra_store_config: Path | None,
|
|
55
|
+
extra_auth_config: Path | None,
|
|
56
|
+
context: str,
|
|
57
|
+
namespace: str,
|
|
58
|
+
k8s_node_name: str,
|
|
59
|
+
custom_data_dir: Path | None = None,
|
|
60
|
+
) -> None:
|
|
61
|
+
super().__init__(server_name, custom_data_dir)
|
|
62
|
+
|
|
63
|
+
self.server_port = server_port
|
|
64
|
+
self.ui_port = ui_port
|
|
65
|
+
self.algorithm_store_port = algorithm_store_port
|
|
66
|
+
self.server_image = server_image
|
|
67
|
+
self.store_image = store_image
|
|
68
|
+
self.ui_image = ui_image
|
|
69
|
+
self.extra_server_config = extra_server_config
|
|
70
|
+
self.extra_store_config = extra_store_config
|
|
71
|
+
self.extra_auth_config = extra_auth_config
|
|
72
|
+
self.context = context
|
|
73
|
+
self.namespace = namespace
|
|
74
|
+
|
|
75
|
+
self.server_config_file = None
|
|
76
|
+
self.store_config_file = None
|
|
77
|
+
self.auth_config_file = None
|
|
78
|
+
self.k8s_node_name = k8s_node_name
|
|
79
|
+
|
|
80
|
+
def generate_server_configs(self) -> None:
|
|
81
|
+
"""Generates the demo network."""
|
|
82
|
+
|
|
83
|
+
self._create_auth_config()
|
|
84
|
+
|
|
85
|
+
self._create_vserver_config()
|
|
86
|
+
|
|
87
|
+
self._create_algo_store_config()
|
|
88
|
+
|
|
89
|
+
def __server_config_return_func(self, extra_config: dict, data_dir: Path) -> dict:
|
|
90
|
+
"""
|
|
91
|
+
Return a dict with server configuration values to be used in creating the
|
|
92
|
+
config file.
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
extra_config : dict
|
|
97
|
+
Extra configuration (parsed from YAML) to be added to the server
|
|
98
|
+
configuration.
|
|
99
|
+
data_dir : Path
|
|
100
|
+
Path to the data directory.
|
|
101
|
+
|
|
102
|
+
Returns
|
|
103
|
+
-------
|
|
104
|
+
dict
|
|
105
|
+
Dictionary with server configuration values.
|
|
106
|
+
"""
|
|
107
|
+
store_service = (
|
|
108
|
+
f"vantage6-{self.server_name}-store-user-algorithm-store-store-service"
|
|
109
|
+
)
|
|
110
|
+
store_address = (
|
|
111
|
+
f"http://{store_service}.{self.namespace}.svc.cluster.local:"
|
|
112
|
+
f"{Ports.DEV_ALGO_STORE}"
|
|
113
|
+
)
|
|
114
|
+
config = {
|
|
115
|
+
"server": {
|
|
116
|
+
"baseUrl": f"{HTTP_LOCALHOST}:{self.server_port}",
|
|
117
|
+
# TODO: v5+ set to latest v5 image
|
|
118
|
+
# TODO make this configurable
|
|
119
|
+
"image": (
|
|
120
|
+
self.server_image
|
|
121
|
+
or "harbor2.vantage6.ai/infrastructure/server:5.0.0a37"
|
|
122
|
+
),
|
|
123
|
+
"algorithm_stores": [
|
|
124
|
+
{
|
|
125
|
+
"name": "Local store",
|
|
126
|
+
"url": store_address,
|
|
127
|
+
"api_path": "/store",
|
|
128
|
+
}
|
|
129
|
+
],
|
|
130
|
+
"logging": {
|
|
131
|
+
"level": "DEBUG",
|
|
132
|
+
},
|
|
133
|
+
"jwt": {
|
|
134
|
+
"secret": "development-constant-secret!",
|
|
135
|
+
},
|
|
136
|
+
"dev": {
|
|
137
|
+
"host_uri": (
|
|
138
|
+
"host.docker.internal"
|
|
139
|
+
if self.k8s_node_name == "docker-desktop"
|
|
140
|
+
else "172.17.0.1"
|
|
141
|
+
),
|
|
142
|
+
"store_address": store_address,
|
|
143
|
+
},
|
|
144
|
+
"keycloakUrl": (
|
|
145
|
+
f"http://vantage6-{self.server_name}-auth-user-auth-keycloak."
|
|
146
|
+
f"{self.namespace}.svc.cluster.local"
|
|
147
|
+
),
|
|
148
|
+
},
|
|
149
|
+
"rabbitmq": {},
|
|
150
|
+
"database": {
|
|
151
|
+
"volumePath": str(data_dir),
|
|
152
|
+
"k8sNodeName": self.k8s_node_name,
|
|
153
|
+
},
|
|
154
|
+
"ui": {
|
|
155
|
+
"port": self.ui_port,
|
|
156
|
+
# TODO: v5+ set to latest v5 image
|
|
157
|
+
# TODO: make this configurable
|
|
158
|
+
"image": (
|
|
159
|
+
self.ui_image or "harbor2.vantage6.ai/infrastructure/ui:5.0.0a37"
|
|
160
|
+
),
|
|
161
|
+
},
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
# merge the extra config with the server config
|
|
165
|
+
if extra_config is not None:
|
|
166
|
+
config.update(extra_config)
|
|
167
|
+
|
|
168
|
+
return config
|
|
169
|
+
|
|
170
|
+
def _create_vserver_config(self) -> None:
|
|
171
|
+
"""Creates server configuration file (YAML)."""
|
|
172
|
+
|
|
173
|
+
data_dir = self._create_and_get_data_dir(instance_type=InstanceType.SERVER)
|
|
174
|
+
|
|
175
|
+
extra_config = self._read_extra_config_file(self.extra_server_config)
|
|
176
|
+
if self.ui_image is not None:
|
|
177
|
+
ui_config = extra_config.get("ui", {}) if extra_config is not None else {}
|
|
178
|
+
ui_config["image"] = self.ui_image
|
|
179
|
+
extra_config["ui"] = ui_config
|
|
180
|
+
|
|
181
|
+
# Create the server config file
|
|
182
|
+
self.server_config_file = new(
|
|
183
|
+
config_producing_func=self.__server_config_return_func,
|
|
184
|
+
config_producing_func_args=(extra_config, data_dir),
|
|
185
|
+
name=self.server_name,
|
|
186
|
+
system_folders=False,
|
|
187
|
+
namespace=self.namespace,
|
|
188
|
+
context=self.context,
|
|
189
|
+
type_=InstanceType.SERVER,
|
|
190
|
+
is_sandbox=True,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
def _create_algo_store_config(self) -> None:
|
|
194
|
+
"""Create algorithm store configuration file (YAML)."""
|
|
195
|
+
|
|
196
|
+
extra_config = self._read_extra_config_file(self.extra_store_config)
|
|
197
|
+
|
|
198
|
+
data_dir = self._create_and_get_data_dir(InstanceType.ALGORITHM_STORE)
|
|
199
|
+
|
|
200
|
+
self.store_config_file = new(
|
|
201
|
+
config_producing_func=self.__algo_store_config_return_func,
|
|
202
|
+
config_producing_func_args=(extra_config, data_dir),
|
|
203
|
+
name=f"{self.server_name}-store",
|
|
204
|
+
system_folders=False,
|
|
205
|
+
namespace=self.namespace,
|
|
206
|
+
context=self.context,
|
|
207
|
+
type_=InstanceType.ALGORITHM_STORE,
|
|
208
|
+
is_sandbox=True,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
def __algo_store_config_return_func(
|
|
212
|
+
self, extra_config: dict, data_dir: Path
|
|
213
|
+
) -> dict:
|
|
214
|
+
"""
|
|
215
|
+
Return a dict with algorithm store configuration values to be used in creating
|
|
216
|
+
the config file.
|
|
217
|
+
|
|
218
|
+
Returns
|
|
219
|
+
-------
|
|
220
|
+
dict
|
|
221
|
+
Dictionary with algorithm store configuration values.
|
|
222
|
+
"""
|
|
223
|
+
config = {
|
|
224
|
+
"store": {
|
|
225
|
+
"internal": {
|
|
226
|
+
"port": self.algorithm_store_port,
|
|
227
|
+
},
|
|
228
|
+
"logging": {
|
|
229
|
+
"level": "DEBUG",
|
|
230
|
+
},
|
|
231
|
+
"vantage6ServerUri": f"{HTTP_LOCALHOST}:{self.server_port}",
|
|
232
|
+
"image": (
|
|
233
|
+
self.store_image
|
|
234
|
+
or "harbor2.vantage6.ai/infrastructure/algorithm-store:5.0.0a37"
|
|
235
|
+
),
|
|
236
|
+
"keycloakUrl": (
|
|
237
|
+
f"http://vantage6-{self.server_name}-auth-user-auth-keycloak."
|
|
238
|
+
f"{self.namespace}.svc.cluster.local"
|
|
239
|
+
),
|
|
240
|
+
"policies": {
|
|
241
|
+
"allowLocalhost": True,
|
|
242
|
+
"assignReviewOwnAlgorithm": True,
|
|
243
|
+
},
|
|
244
|
+
"dev": {
|
|
245
|
+
"host_uri": (
|
|
246
|
+
"host.docker.internal"
|
|
247
|
+
if self.k8s_node_name == "docker-desktop"
|
|
248
|
+
else "172.17.0.1"
|
|
249
|
+
),
|
|
250
|
+
"disable_review": True,
|
|
251
|
+
"review_own_algorithm": True,
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
"database": {
|
|
255
|
+
"volumePath": str(data_dir),
|
|
256
|
+
"k8sNodeName": self.k8s_node_name,
|
|
257
|
+
},
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
# merge the extra config with the algorithm store config
|
|
261
|
+
if extra_config is not None:
|
|
262
|
+
config.update(extra_config)
|
|
263
|
+
|
|
264
|
+
return config
|
|
265
|
+
|
|
266
|
+
def _create_auth_config(self) -> None:
|
|
267
|
+
"""Create auth configuration file (YAML)."""
|
|
268
|
+
self.auth_config_file = new(
|
|
269
|
+
config_producing_func=self.__auth_config_return_func,
|
|
270
|
+
config_producing_func_args=(self.extra_auth_config,),
|
|
271
|
+
name=f"{self.server_name}-auth",
|
|
272
|
+
system_folders=False,
|
|
273
|
+
namespace=self.namespace,
|
|
274
|
+
context=self.context,
|
|
275
|
+
type_=InstanceType.AUTH,
|
|
276
|
+
is_sandbox=True,
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
def __auth_config_return_func(self, extra_config: dict) -> dict:
|
|
280
|
+
"""
|
|
281
|
+
Return a dict with auth configuration values to be used in creating the
|
|
282
|
+
config file.
|
|
283
|
+
"""
|
|
284
|
+
|
|
285
|
+
config = {
|
|
286
|
+
"keycloak": {
|
|
287
|
+
"production": False,
|
|
288
|
+
"no_password_update_required": True,
|
|
289
|
+
"redirectUris": [
|
|
290
|
+
f"{HTTP_LOCALHOST}:7600",
|
|
291
|
+
f"{HTTP_LOCALHOST}:7681",
|
|
292
|
+
],
|
|
293
|
+
},
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
# merge the extra config with the auth config
|
|
297
|
+
if extra_config is not None:
|
|
298
|
+
config.update(extra_config)
|
|
299
|
+
|
|
300
|
+
return config
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from importlib import resources as impresources
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from colorama import Fore, Style
|
|
7
|
+
|
|
8
|
+
from vantage6.common import error, info
|
|
9
|
+
from vantage6.common.globals import InstanceType
|
|
10
|
+
|
|
11
|
+
import vantage6.cli.sandbox.data as node_datafiles_dir
|
|
12
|
+
from vantage6.cli.common.new import new
|
|
13
|
+
from vantage6.cli.context.node import NodeContext
|
|
14
|
+
from vantage6.cli.globals import (
|
|
15
|
+
DefaultDatasets,
|
|
16
|
+
)
|
|
17
|
+
from vantage6.cli.sandbox.config.base import BaseSandboxConfigManager
|
|
18
|
+
from vantage6.cli.sandbox.populate.helpers.utils import replace_wsl_path
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class NodeDataset:
|
|
23
|
+
label: str
|
|
24
|
+
path: Path
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class NodeSandboxConfigManager(BaseSandboxConfigManager):
|
|
28
|
+
"""
|
|
29
|
+
Class to store the node sandbox configurations.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
server_name : str
|
|
34
|
+
Name of the server.
|
|
35
|
+
api_keys : list[str]
|
|
36
|
+
List of API keys.
|
|
37
|
+
node_names : list[str]
|
|
38
|
+
List of node names.
|
|
39
|
+
server_port : int
|
|
40
|
+
Port of the server.
|
|
41
|
+
node_image : str | None
|
|
42
|
+
Image of the node.
|
|
43
|
+
extra_node_config : Path | None
|
|
44
|
+
Path to the extra node configuration file.
|
|
45
|
+
extra_dataset : NodeDataset | None
|
|
46
|
+
List of tuples with the label and path to the dataset file.
|
|
47
|
+
context : str
|
|
48
|
+
Kubernetes context.
|
|
49
|
+
namespace : str
|
|
50
|
+
Kubernetes namespace.
|
|
51
|
+
k8s_node_name : str
|
|
52
|
+
Kubernetes node name.
|
|
53
|
+
custom_data_dir : Path | None
|
|
54
|
+
Path to the custom data directory. Useful on WSL because of mount issues for
|
|
55
|
+
default directories.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
server_name: str,
|
|
61
|
+
api_keys: list[str],
|
|
62
|
+
node_names: list[str],
|
|
63
|
+
server_port: int,
|
|
64
|
+
node_image: str | None,
|
|
65
|
+
extra_node_config: Path | None,
|
|
66
|
+
extra_dataset: NodeDataset | None,
|
|
67
|
+
context: str,
|
|
68
|
+
namespace: str,
|
|
69
|
+
k8s_node_name: str,
|
|
70
|
+
custom_data_dir: Path | None,
|
|
71
|
+
) -> None:
|
|
72
|
+
super().__init__(server_name, custom_data_dir)
|
|
73
|
+
self.api_keys = api_keys
|
|
74
|
+
self.node_names = node_names
|
|
75
|
+
self.num_nodes = len(api_keys)
|
|
76
|
+
self.server_port = server_port
|
|
77
|
+
self.node_image = node_image
|
|
78
|
+
self.extra_node_config = extra_node_config
|
|
79
|
+
if extra_dataset:
|
|
80
|
+
self.node_datasets = [extra_dataset]
|
|
81
|
+
else:
|
|
82
|
+
self.node_datasets = []
|
|
83
|
+
self.context = context
|
|
84
|
+
self.namespace = namespace
|
|
85
|
+
self.k8s_node_name = k8s_node_name
|
|
86
|
+
|
|
87
|
+
self.node_configs = []
|
|
88
|
+
self.node_config_files = []
|
|
89
|
+
self.node_config_names = []
|
|
90
|
+
self.extra_config = None
|
|
91
|
+
|
|
92
|
+
def generate_node_configs(self) -> None:
|
|
93
|
+
"""
|
|
94
|
+
Generates ``num_nodes`` node configuration files.
|
|
95
|
+
"""
|
|
96
|
+
node_data_files = []
|
|
97
|
+
self.extra_config = self._read_extra_config_file(self.extra_node_config)
|
|
98
|
+
|
|
99
|
+
data_directory = impresources.files(node_datafiles_dir)
|
|
100
|
+
|
|
101
|
+
# Add default datasets to the list of dataset provided
|
|
102
|
+
for default_dataset in DefaultDatasets:
|
|
103
|
+
# note that the label is the name of the dataset with the underscores
|
|
104
|
+
# replace with hyphens, to make it valid for k8s
|
|
105
|
+
self.node_datasets.append(
|
|
106
|
+
NodeDataset(
|
|
107
|
+
label=default_dataset.name.lower().replace("_", "-"),
|
|
108
|
+
path=data_directory / default_dataset.value,
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Check for duplicate dataset labels
|
|
113
|
+
seen_labels = set()
|
|
114
|
+
duplicates = [
|
|
115
|
+
label
|
|
116
|
+
for label in [dataset.label for dataset in self.node_datasets]
|
|
117
|
+
if (label in seen_labels or seen_labels.add(label))
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
if len(duplicates) > 0:
|
|
121
|
+
error(
|
|
122
|
+
f"Duplicate dataset labels found: {duplicates}. "
|
|
123
|
+
f"Please make sure all dataset labels are unique."
|
|
124
|
+
)
|
|
125
|
+
exit(1)
|
|
126
|
+
|
|
127
|
+
# create the data files for the nodes and get the path and label for each
|
|
128
|
+
# dataset
|
|
129
|
+
for dataset in self.node_datasets:
|
|
130
|
+
node_data_files.append(self._create_node_data_files(dataset))
|
|
131
|
+
|
|
132
|
+
for idx, api_key in enumerate(self.api_keys):
|
|
133
|
+
config = {
|
|
134
|
+
"org_id": idx + 1,
|
|
135
|
+
"api_key": api_key,
|
|
136
|
+
"node_name": self.node_names[idx],
|
|
137
|
+
}
|
|
138
|
+
config_file = self._create_node_config_file(
|
|
139
|
+
config,
|
|
140
|
+
[files[idx] for files in node_data_files],
|
|
141
|
+
)
|
|
142
|
+
self.node_configs.append(config)
|
|
143
|
+
self.node_config_files.append(config_file)
|
|
144
|
+
info(
|
|
145
|
+
f"Created {Fore.GREEN}{len(self.node_config_files)}{Style.RESET_ALL} node "
|
|
146
|
+
f"configuration(s), attaching them to {Fore.GREEN}{self.server_name}"
|
|
147
|
+
f"{Style.RESET_ALL}."
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def _create_node_data_files(
|
|
151
|
+
self, node_dataset: NodeDataset
|
|
152
|
+
) -> list[tuple[str, Path]]:
|
|
153
|
+
"""
|
|
154
|
+
Create data files for nodes.
|
|
155
|
+
|
|
156
|
+
Parameters
|
|
157
|
+
----------
|
|
158
|
+
node_dataset : NodeDataset
|
|
159
|
+
Tuple with the label and path to the dataset file.
|
|
160
|
+
|
|
161
|
+
Returns
|
|
162
|
+
-------
|
|
163
|
+
list[tuple[str, Path]]
|
|
164
|
+
List of tuples with the label and path to the dataset file.
|
|
165
|
+
"""
|
|
166
|
+
info(
|
|
167
|
+
f"Creating data files using dataset '{node_dataset.label}' for "
|
|
168
|
+
f"{self.num_nodes} nodes"
|
|
169
|
+
)
|
|
170
|
+
data_files = []
|
|
171
|
+
full_df = pd.read_csv(node_dataset.path)
|
|
172
|
+
length_df = len(full_df)
|
|
173
|
+
for i in range(self.num_nodes):
|
|
174
|
+
node_name = f"{self.server_name}_node_{i + 1}"
|
|
175
|
+
path_to_dev_dir = self._create_and_get_data_dir(
|
|
176
|
+
InstanceType.NODE, is_data_folder=False
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# Split the data over the nodes
|
|
180
|
+
start = i * length_df // self.num_nodes
|
|
181
|
+
end = (i + 1) * length_df // self.num_nodes
|
|
182
|
+
data = full_df[start:end]
|
|
183
|
+
data_file = (
|
|
184
|
+
replace_wsl_path(path_to_dev_dir, to_mnt_wsl=True)
|
|
185
|
+
/ f"df_{node_dataset.label}_{node_name}.csv"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
# write data to file
|
|
189
|
+
data.to_csv(data_file, index=False)
|
|
190
|
+
data_files.append(
|
|
191
|
+
(node_dataset.label, replace_wsl_path(data_file, to_mnt_wsl=False))
|
|
192
|
+
)
|
|
193
|
+
return data_files
|
|
194
|
+
|
|
195
|
+
def _create_node_config_file(
|
|
196
|
+
self, config: dict, datasets: list[tuple[str, Path]]
|
|
197
|
+
) -> Path:
|
|
198
|
+
"""
|
|
199
|
+
Create a node configuration file (YAML).
|
|
200
|
+
|
|
201
|
+
Parameters
|
|
202
|
+
----------
|
|
203
|
+
config : dict
|
|
204
|
+
Configuration dictionary.
|
|
205
|
+
datasets : list[tuple[str, Path]]
|
|
206
|
+
List of tuples with the label and path to the dataset file.
|
|
207
|
+
|
|
208
|
+
Returns
|
|
209
|
+
-------
|
|
210
|
+
Path
|
|
211
|
+
Path to the node configuration file.
|
|
212
|
+
"""
|
|
213
|
+
node_name = config["node_name"]
|
|
214
|
+
config_name = f"{self.server_name}-{node_name}"
|
|
215
|
+
|
|
216
|
+
path_to_data_dir = self._create_and_get_data_dir(
|
|
217
|
+
InstanceType.NODE, is_data_folder=True, node_name=node_name
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
# delete old node config if it exists
|
|
221
|
+
NodeContext.remove_config_file_if_exists(
|
|
222
|
+
InstanceType.NODE, config_name, False, is_sandbox=True
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# create new node config
|
|
226
|
+
node_config = new(
|
|
227
|
+
config_producing_func=self.__node_config_return_func,
|
|
228
|
+
config_producing_func_args=(
|
|
229
|
+
config,
|
|
230
|
+
self.extra_config,
|
|
231
|
+
datasets,
|
|
232
|
+
path_to_data_dir,
|
|
233
|
+
),
|
|
234
|
+
name=config_name,
|
|
235
|
+
system_folders=False,
|
|
236
|
+
namespace=self.namespace,
|
|
237
|
+
context=self.context,
|
|
238
|
+
type_=InstanceType.NODE,
|
|
239
|
+
is_sandbox=True,
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
self.node_config_names.append(config_name)
|
|
243
|
+
|
|
244
|
+
return node_config
|
|
245
|
+
|
|
246
|
+
def __node_config_return_func(
|
|
247
|
+
self,
|
|
248
|
+
node_specific_config: dict,
|
|
249
|
+
extra_config: dict,
|
|
250
|
+
datasets: list[tuple[str, Path]],
|
|
251
|
+
path_to_data_dir: Path,
|
|
252
|
+
) -> dict:
|
|
253
|
+
"""
|
|
254
|
+
Return a dict with node configuration values to be used in creating the
|
|
255
|
+
config file.
|
|
256
|
+
"""
|
|
257
|
+
config = {
|
|
258
|
+
"node": {
|
|
259
|
+
"proxyPort": 7676 + int(node_specific_config["org_id"]),
|
|
260
|
+
"apiKey": node_specific_config["api_key"],
|
|
261
|
+
"name": node_specific_config["node_name"],
|
|
262
|
+
"image": (
|
|
263
|
+
self.node_image
|
|
264
|
+
# TODO v5+ update
|
|
265
|
+
or "harbor2.vantage6.ai/infrastructure/node:5.0.0a37"
|
|
266
|
+
),
|
|
267
|
+
"logging": {
|
|
268
|
+
"level": "DEBUG",
|
|
269
|
+
"file": f"{node_specific_config['node_name']}.log",
|
|
270
|
+
},
|
|
271
|
+
"keycloakUrl": (
|
|
272
|
+
f"http://vantage6-{self.server_name}-auth-user-auth-keycloak."
|
|
273
|
+
f"{self.namespace}.svc.cluster.local"
|
|
274
|
+
),
|
|
275
|
+
"persistence": {
|
|
276
|
+
"tasks": {
|
|
277
|
+
"hostPath": str(path_to_data_dir),
|
|
278
|
+
"size": "1Gi",
|
|
279
|
+
},
|
|
280
|
+
"database": {
|
|
281
|
+
"size": "1Gi",
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
"k8sNodeName": self.k8s_node_name,
|
|
285
|
+
"databases": {
|
|
286
|
+
"fileBased": [
|
|
287
|
+
{
|
|
288
|
+
"name": dataset[0],
|
|
289
|
+
"uri": dataset[1],
|
|
290
|
+
"type": "csv",
|
|
291
|
+
"volumePath": Path(dataset[1]).parent,
|
|
292
|
+
"originalName": dataset[0],
|
|
293
|
+
}
|
|
294
|
+
for dataset in [datasets[0]]
|
|
295
|
+
]
|
|
296
|
+
},
|
|
297
|
+
"server": {
|
|
298
|
+
"url": (
|
|
299
|
+
f"http://vantage6-{self.server_name}-user-server-"
|
|
300
|
+
f"vantage6-server-service.{self.namespace}.svc.cluster.local"
|
|
301
|
+
),
|
|
302
|
+
"port": self.server_port,
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
# merge the extra config with the node config
|
|
308
|
+
if extra_config is not None:
|
|
309
|
+
config.update(extra_config)
|
|
310
|
+
|
|
311
|
+
return config
|