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
@@ -1,111 +1,112 @@
1
- import subprocess
2
- import itertools
3
- from shutil import rmtree
4
- from pathlib import Path
1
+ # import subprocess
2
+ # import itertools
3
+ # from shutil import rmtree
4
+ # from pathlib import Path
5
5
 
6
- import click
7
- import docker
8
- from colorama import Fore, Style
6
+ # import click
7
+ # import docker
8
+ # from colorama import Fore, Style
9
9
 
10
- from vantage6.cli.context.algorithm_store import AlgorithmStoreContext
11
- from vantage6.common import info, error
12
- from vantage6.common.globals import APPNAME
13
- from vantage6.cli.context.server import ServerContext
14
- from vantage6.cli.context.node import NodeContext
15
- from vantage6.cli.server.remove import cli_server_remove
16
- from vantage6.cli.utils import remove_file
17
- from vantage6.common.globals import InstanceType
18
- from vantage6.cli.dev.utils import get_dev_server_context
10
+ # from vantage6.cli.context.algorithm_store import AlgorithmStoreContext
11
+ # from vantage6.common import info, error
12
+ # from vantage6.common.globals import APPNAME
13
+ # from vantage6.cli.context.server import ServerContext
14
+ # from vantage6.cli.context.node import NodeContext
15
+ # from vantage6.cli.server.remove import cli_server_remove
16
+ # from vantage6.cli.utils import remove_file
17
+ # from vantage6.common.globals import InstanceType
19
18
 
19
+ # # from vantage6.cli.dev.utils import get_dev_server_context
20
20
 
21
- @click.command()
22
- @click.option("-n", "--name", default=None, help="Name of the configuration.")
23
- @click.option(
24
- "-c",
25
- "--config",
26
- default=None,
27
- help="Path to configuration-file; overrides --name",
28
- )
29
- @click.pass_context
30
- def remove_demo_network(
31
- click_ctx: click.Context, name: str | None, config: str | None
32
- ) -> None:
33
- """Remove all related demo network files and folders.
34
21
 
35
- Select a server configuration to remove that server and the nodes attached
36
- to it.
37
- """
38
- ctx = get_dev_server_context(config, name)
22
+ # @click.command()
23
+ # @click.option("-n", "--name", default=None, help="Name of the configuration.")
24
+ # @click.option(
25
+ # "-c",
26
+ # "--config",
27
+ # default=None,
28
+ # help="Path to configuration-file; overrides --name",
29
+ # )
30
+ # @click.pass_context
31
+ # def remove_demo_network(
32
+ # click_ctx: click.Context, name: str | None, config: str | None
33
+ # ) -> None:
34
+ # """Remove all related demo network files and folders.
39
35
 
40
- # check that the server is not running
41
- client = docker.from_env()
42
- running_servers = client.containers.list(
43
- filters={"label": f"{APPNAME}-type={InstanceType.SERVER.value}"}
44
- )
45
- running_server_names = [server.name for server in running_servers]
46
- container_name = f"{APPNAME}-{name}-user-{InstanceType.SERVER.value}"
47
- if container_name in running_server_names:
48
- error(
49
- f"Server {Fore.RED}{name}{Style.RESET_ALL} is still running! First stop "
50
- "the network with 'v6 dev stop-demo-network'."
51
- )
52
- return
36
+ # Select a server configuration to remove that server and the nodes attached
37
+ # to it.
38
+ # """
39
+ # ctx = get_dev_server_context(config, name)
53
40
 
54
- # remove the server
55
- for handler in itertools.chain(ctx.log.handlers, ctx.log.root.handlers):
56
- handler.close()
57
- click_ctx.invoke(cli_server_remove, ctx=ctx, force=True)
41
+ # # check that the server is not running
42
+ # client = docker.from_env()
43
+ # running_servers = client.containers.list(
44
+ # filters={"label": f"{APPNAME}-type={InstanceType.SERVER}"}
45
+ # )
46
+ # running_server_names = [server.name for server in running_servers]
47
+ # container_name = f"{APPNAME}-{name}-user-{InstanceType.SERVER}"
48
+ # if container_name in running_server_names:
49
+ # error(
50
+ # f"Server {Fore.RED}{name}{Style.RESET_ALL} is still running! First stop "
51
+ # "the network with 'v6 dev stop-demo-network'."
52
+ # )
53
+ # return
58
54
 
59
- # removing the server import config
60
- info("Deleting demo import config file")
61
- server_configs = ServerContext.instance_folders(
62
- InstanceType.SERVER, ctx.name, system_folders=False
63
- )
64
- import_config_to_del = Path(server_configs["dev"]) / f"{ctx.name}.yaml"
65
- remove_file(import_config_to_del, "import_configuration")
55
+ # # remove the server
56
+ # for handler in itertools.chain(ctx.log.handlers, ctx.log.root.handlers):
57
+ # handler.close()
58
+ # click_ctx.invoke(cli_server_remove, ctx=ctx, force=True)
66
59
 
67
- # also remove the server folder
68
- server_configs = ServerContext.instance_folders(
69
- InstanceType.SERVER, ctx.name, system_folders=False
70
- )
71
- server_folder = server_configs["data"]
72
- if server_folder.is_dir():
73
- rmtree(server_folder)
60
+ # # removing the server import config
61
+ # info("Deleting demo import config file")
62
+ # server_configs = ServerContext.instance_folders(
63
+ # InstanceType.SERVER, ctx.name, system_folders=False
64
+ # )
65
+ # import_config_to_del = Path(server_configs["dev"]) / f"{ctx.name}.yaml"
66
+ # remove_file(import_config_to_del, "import_configuration")
74
67
 
75
- # remove the store folder
76
- store_configs = AlgorithmStoreContext.instance_folders(
77
- InstanceType.ALGORITHM_STORE, f"{ctx.name}_store", system_folders=False
78
- )
79
- store_folder = store_configs["data"]
80
- if store_folder.is_dir():
81
- rmtree(store_folder)
68
+ # # also remove the server folder
69
+ # server_configs = ServerContext.instance_folders(
70
+ # InstanceType.SERVER, ctx.name, system_folders=False
71
+ # )
72
+ # server_folder = server_configs["data"]
73
+ # if server_folder.is_dir():
74
+ # rmtree(server_folder)
82
75
 
83
- # remove the store config file
84
- subprocess.run(
85
- [
86
- "v6",
87
- "algorithm-store",
88
- "remove",
89
- "-n",
90
- f"{ctx.name}_store",
91
- "--force",
92
- "--user",
93
- ]
94
- )
76
+ # # remove the store folder
77
+ # store_configs = AlgorithmStoreContext.instance_folders(
78
+ # InstanceType.ALGORITHM_STORE, f"{ctx.name}_store", system_folders=False
79
+ # )
80
+ # store_folder = store_configs["data"]
81
+ # if store_folder.is_dir():
82
+ # rmtree(store_folder)
95
83
 
96
- # remove the nodes
97
- configs, _ = NodeContext.available_configurations(system_folders=False)
98
- node_names = [
99
- config.name for config in configs if config.name.startswith(f"{ctx.name}_node_")
100
- ]
101
- for name in node_names:
102
- node_ctx = NodeContext(name, False)
103
- for handler in itertools.chain(
104
- node_ctx.log.handlers, node_ctx.log.root.handlers
105
- ):
106
- handler.close()
107
- subprocess.run(["v6", "node", "remove", "-n", name, "--user", "--force"])
84
+ # # remove the store config file
85
+ # subprocess.run(
86
+ # [
87
+ # "v6",
88
+ # "algorithm-store",
89
+ # "remove",
90
+ # "-n",
91
+ # f"{ctx.name}_store",
92
+ # "--force",
93
+ # "--user",
94
+ # ]
95
+ # )
108
96
 
109
- # remove data files attached to the network
110
- data_dirs_nodes = NodeContext.instance_folders("node", "", False)["dev"]
111
- rmtree(Path(data_dirs_nodes / ctx.name))
97
+ # # remove the nodes
98
+ # configs, _ = NodeContext.available_configurations(system_folders=False)
99
+ # node_names = [
100
+ # config.name for config in configs if config.name.startswith(f"{ctx.name}_node_")
101
+ # ]
102
+ # for name in node_names:
103
+ # node_ctx = NodeContext(name, False)
104
+ # for handler in itertools.chain(
105
+ # node_ctx.log.handlers, node_ctx.log.root.handlers
106
+ # ):
107
+ # handler.close()
108
+ # subprocess.run(["v6", "node", "remove", "-n", name, "--user", "--force"])
109
+
110
+ # # remove data files attached to the network
111
+ # data_dirs_nodes = NodeContext.instance_folders("node", "", False)["dev"]
112
+ # rmtree(Path(data_dirs_nodes / ctx.name))
vantage6/cli/globals.py CHANGED
@@ -2,10 +2,18 @@
2
2
  This module contains global variables that are used throughout the CLI.
3
3
  """
4
4
 
5
- from enum import Enum
6
5
  from pathlib import Path
6
+
7
+ from vantage6.common.enum import StrEnumBase
7
8
  from vantage6.common.globals import APPNAME
8
9
 
10
+ #
11
+ # CLI SETTINGS
12
+ #
13
+
14
+ DEFAULT_CLI_CONFIG_FOLDER = Path.home() / ".vantage6"
15
+ DEFAULT_CLI_CONFIG_FILE = DEFAULT_CLI_CONFIG_FOLDER / "config.yaml"
16
+
9
17
  #
10
18
  # SERVER SETTINGS
11
19
  #
@@ -44,22 +52,34 @@ DIAGNOSTICS_IMAGE = "harbor2.vantage6.ai/algorithms/diagnostic"
44
52
  # Address of community algorithm store
45
53
  COMMUNITY_STORE = "https://store.cotopaxi.vantage6.ai/api"
46
54
 
55
+ DEFAULT_PROMETHEUS_IMAGE = "prom/prometheus"
56
+ PROMETHEUS_CONFIG = "prometheus.yml"
57
+ PROMETHEUS_DIR = "prometheus"
58
+
59
+
60
+ # datasets included in the nodes of the dev network
61
+ class DefaultDatasets(StrEnumBase):
62
+ """Enum containing default datasets"""
63
+
64
+ OLYMPIC_ATHLETES = "olympic_athletes_2016.csv"
65
+ KAPLAN_MEIER_TEST = "km_dataset.csv"
66
+
47
67
 
48
- class ServerType(str, Enum):
68
+ class ServerType(StrEnumBase):
49
69
  """Enum containing server types"""
50
70
 
51
71
  V6SERVER = "server"
52
72
  ALGORITHM_STORE = "algorithm-store"
53
73
 
54
74
 
55
- class ServerGlobals(str, Enum):
75
+ class ServerGlobals(StrEnumBase):
56
76
  """Enum containing server environment variables"""
57
77
 
58
78
  DB_URI_ENV_VAR = "VANTAGE6_DB_URI"
59
79
  CONFIG_NAME_ENV_VAR = "VANTAGE6_CONFIG_NAME"
60
80
 
61
81
 
62
- class AlgoStoreGlobals(str, Enum):
82
+ class AlgoStoreGlobals(StrEnumBase):
63
83
  """Enum containing algorithm store environment variables"""
64
84
 
65
85
  DB_URI_ENV_VAR = "VANTAGE6_ALGO_STORE_DB_URI"
@@ -3,15 +3,17 @@ Common functions that are used in node CLI commands
3
3
  """
4
4
 
5
5
  import os
6
+
6
7
  import docker
7
8
  from colorama import Fore, Style
8
9
 
9
- from vantage6.common import error, info, debug
10
+ from vantage6.common import debug, error, info
10
11
  from vantage6.common.globals import APPNAME, InstanceType, RequiredNodeEnvVars
12
+
11
13
  from vantage6.client import UserClient
12
14
 
13
- from vantage6.cli.context.node import NodeContext
14
15
  from vantage6.cli.configuration_wizard import select_configuration_questionaire
16
+ from vantage6.cli.context.node import NodeContext
15
17
 
16
18
 
17
19
  def create_client(ctx: NodeContext) -> UserClient:
@@ -86,8 +88,7 @@ def select_node(name: str, system_folders: bool) -> tuple[str, str]:
86
88
  # raise error if config could not be found
87
89
  if not NodeContext.config_exists(name, system_folders):
88
90
  error(
89
- f"The configuration {Fore.RED}{name}{Style.RESET_ALL} could "
90
- f"not be found."
91
+ f"The configuration {Fore.RED}{name}{Style.RESET_ALL} could not be found."
91
92
  )
92
93
  exit(1)
93
94
  return name
@@ -108,6 +109,6 @@ def find_running_node_names(client: docker.DockerClient) -> list[str]:
108
109
  List of names of running nodes
109
110
  """
110
111
  running_nodes = client.containers.list(
111
- filters={"label": f"{APPNAME}-type={InstanceType.NODE.value}"}
112
+ filters={"label": f"{APPNAME}-type={InstanceType.NODE}"}
112
113
  )
113
114
  return [node.name for node in running_nodes]
vantage6/cli/node/new.py CHANGED
@@ -1,12 +1,13 @@
1
1
  import click
2
2
  from colorama import Fore, Style
3
3
 
4
- from vantage6.common import error, info, ensure_config_dir_writable
4
+ from vantage6.common import ensure_config_dir_writable, error, info
5
+ from vantage6.common.globals import InstanceType
6
+
7
+ from vantage6.cli.configuration_wizard import configuration_wizard
5
8
  from vantage6.cli.context.node import NodeContext
6
9
  from vantage6.cli.globals import DEFAULT_NODE_SYSTEM_FOLDERS as N_FOL
7
- from vantage6.cli.configuration_wizard import configuration_wizard
8
10
  from vantage6.cli.utils import check_config_name_allowed, prompt_config_name
9
- from vantage6.common.globals import InstanceType
10
11
 
11
12
 
12
13
  @click.command()
@@ -14,14 +14,16 @@ from vantage6.common import (
14
14
  )
15
15
  from vantage6.common.globals import APPNAME
16
16
 
17
- from vantage6.common.globals import VPN_CONFIG_FILE
18
-
19
17
 
20
18
  from vantage6.cli.context.node import NodeContext
21
19
  from vantage6.cli.globals import DEFAULT_NODE_SYSTEM_FOLDERS as N_FOL
22
20
  from vantage6.cli.utils import check_if_docker_daemon_is_running, remove_file
23
21
  from vantage6.cli.node.common import select_node, find_running_node_names
24
22
 
23
+ # TODO v5+ remove this - just a dummy to prevent import issues from v4 CLI
24
+ # from vantage6.common.globals import VPN_CONFIG_FILE
25
+ VPN_CONFIG_FILE = "vpn.conf"
26
+
25
27
 
26
28
  @click.command()
27
29
  @click.option("-n", "--name", default=None, help="Configuration name")
@@ -1,15 +1,18 @@
1
+ import os.path
2
+ import time
1
3
  from pathlib import Path
2
4
  from threading import Thread
3
- import time
4
- import os.path
5
5
 
6
6
  import click
7
7
  import docker
8
-
9
8
  from colorama import Fore, Style
10
9
 
11
- from vantage6.cli.common.start import pull_infra_image
12
- from vantage6.common import warning, error, info, debug, get_database_config
10
+ from vantage6.common import debug, error, info, warning
11
+ from vantage6.common.dataclass import TaskDB
12
+ from vantage6.common.docker.addons import (
13
+ check_docker_running,
14
+ remove_container_if_exists,
15
+ )
13
16
  from vantage6.common.globals import (
14
17
  APPNAME,
15
18
  DEFAULT_DOCKER_REGISTRY,
@@ -17,17 +20,14 @@ from vantage6.common.globals import (
17
20
  DEFAULT_NODE_IMAGE_WO_TAG,
18
21
  InstanceType,
19
22
  )
20
- from vantage6.common.docker.addons import (
21
- remove_container_if_exists,
22
- check_docker_running,
23
- )
24
23
 
24
+ from vantage6.cli import __version__
25
25
  from vantage6.cli.common.decorator import click_insert_context
26
- from vantage6.cli.context.node import NodeContext
26
+ from vantage6.cli.common.start import pull_infra_image
27
27
  from vantage6.cli.common.utils import print_log_worker
28
+ from vantage6.cli.context.node import NodeContext
28
29
  from vantage6.cli.node.common import create_client
29
30
  from vantage6.cli.utils import check_config_name_allowed
30
- from vantage6.cli import __version__
31
31
 
32
32
 
33
33
  @click.command()
@@ -40,18 +40,17 @@ from vantage6.cli import __version__
40
40
  @click.option(
41
41
  "--force-db-mount",
42
42
  is_flag=True,
43
- help="Always mount node databases; skip the check if they are " "existing files.",
43
+ help="Always mount node databases; skip the check if they are existing files.",
44
44
  )
45
45
  @click.option(
46
46
  "--attach/--detach",
47
47
  default=False,
48
- help="Show node logs on the current console after starting the " "node",
48
+ help="Show node logs on the current console after starting the node",
49
49
  )
50
50
  @click.option(
51
51
  "--mount-src",
52
52
  default="",
53
- help="Override vantage6 source code in container with the source"
54
- " code in this path",
53
+ help="Override vantage6 source code in container with the source code in this path",
55
54
  )
56
55
  @click_insert_context(InstanceType.NODE, include_name=True, include_system_folders=True)
57
56
  def cli_node_start(
@@ -78,7 +77,7 @@ def cli_node_start(
78
77
 
79
78
  # check that this node is not already running
80
79
  running_nodes = docker_client.containers.list(
81
- filters={"label": f"{APPNAME}-type={InstanceType.NODE.value}"}
80
+ filters={"label": f"{APPNAME}-type={InstanceType.NODE}"}
82
81
  )
83
82
 
84
83
  suffix = "system" if system_folders else "user"
@@ -116,9 +115,7 @@ def cli_node_start(
116
115
  f":{major_minor}"
117
116
  )
118
117
  except Exception:
119
- warning(
120
- "Could not determine server version. Using default " "node image"
121
- )
118
+ warning("Could not determine server version. Using default node image")
122
119
 
123
120
  if major_minor and not __version__.startswith(major_minor):
124
121
  warning(
@@ -213,29 +210,25 @@ def cli_node_start(
213
210
 
214
211
  # only mount the DB if it is a file
215
212
  info("Setting up databases")
216
- db_labels = [db["label"] for db in ctx.databases]
217
- for label in db_labels:
213
+ dbs = [TaskDB.from_dict(db) for db in ctx.databases]
214
+ for db in dbs:
218
215
  # check that label contains only valid characters
219
- if not label.isidentifier():
216
+ if not db.label.isidentifier():
220
217
  error(
221
- f"Database label {Fore.RED}{label}{Style.RESET_ALL} contains"
218
+ f"Database label {Fore.RED}{db.label}{Style.RESET_ALL} contains"
222
219
  " invalid characters. Only letters, numbers, and underscores"
223
220
  " are allowed, and it cannot start with a number."
224
221
  )
225
222
  exit(1)
226
223
 
227
- db_config = get_database_config(ctx.databases, label)
228
- uri = db_config["uri"]
229
- db_type = db_config["type"]
230
-
231
224
  info(
232
- f" Processing {Fore.GREEN}{db_type}{Style.RESET_ALL} database "
233
- f"{Fore.GREEN}{label}:{uri}{Style.RESET_ALL}"
225
+ f" Processing {Fore.GREEN}{db.type}{Style.RESET_ALL} database "
226
+ f"{Fore.GREEN}{db.label}:{db.uri}{Style.RESET_ALL}"
234
227
  )
235
- label_capitals = label.upper()
228
+ label_capitals = db.label.upper()
236
229
 
237
230
  try:
238
- db_file_exists = Path(uri).exists()
231
+ db_file_exists = Path(db.uri).exists()
239
232
  except Exception:
240
233
  # If the database uri cannot be parsed, it is definitely not a
241
234
  # file. In case of http servers or sql servers, checking the path
@@ -243,22 +236,22 @@ def cli_node_start(
243
236
  # we catch all exceptions here.
244
237
  db_file_exists = False
245
238
 
246
- if db_type in ["folder", "csv", "parquet", "excel"] and not db_file_exists:
239
+ if db.type.is_file_based() and not db_file_exists:
247
240
  error(
248
- f"Database {Fore.RED}{uri}{Style.RESET_ALL} not found. Databases of "
249
- f"type '{db_type}' must be present on the harddrive. Please update "
250
- "your node configuration file."
241
+ f"Database {Fore.RED}{db.uri}{Style.RESET_ALL} not found. Databases of "
242
+ f"type '{db.type}' must be present on the harddrive. Please "
243
+ "update your node configuration file."
251
244
  )
252
245
  exit(1)
253
246
 
254
247
  if not db_file_exists and not force_db_mount:
255
248
  debug(" - non file-based database added")
256
- env[f"{label_capitals}_DATABASE_URI"] = uri
249
+ env[f"{label_capitals}_DATABASE_URI"] = db.uri
257
250
  else:
258
251
  debug(" - file-based database added")
259
- suffix = Path(uri).suffix
260
- env[f"{label_capitals}_DATABASE_URI"] = f"{label}{suffix}"
261
- mounts.append((f"/mnt/{label}{suffix}", str(uri)))
252
+ suffix = Path(db.uri).suffix
253
+ env[f"{label_capitals}_DATABASE_URI"] = f"{db.label}{suffix}"
254
+ mounts.append((f"/mnt/{db.label}{suffix}", str(db.uri)))
262
255
 
263
256
  system_folders_option = "--system" if system_folders else "--user"
264
257
  cmd = (
@@ -286,9 +279,7 @@ def cli_node_start(
286
279
  # we won't accept overwrites of existing env vars
287
280
  env_overwrites = extra_env.keys() & env.keys()
288
281
  if env_overwrites:
289
- error(
290
- "Cannot overwrite existing node environment variables: " f"{env_overwrites}"
291
- )
282
+ error(f"Cannot overwrite existing node environment variables: {env_overwrites}")
292
283
  exit(1)
293
284
  env.update(extra_env)
294
285
 
@@ -0,0 +1,146 @@
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(__file__).parent / 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 "
100
+ "target. Ensure Prometheus is in the same Docker network to resolve "
101
+ "this address."
102
+ )
103
+
104
+ with open(self.config_file, "r") as f:
105
+ config = yaml.safe_load(f)
106
+
107
+ job_name = "vantage6_server_metrics"
108
+ job_exists = any(
109
+ job.get("job_name") == job_name
110
+ for job in config.get("scrape_configs", [])
111
+ )
112
+
113
+ if not job_exists:
114
+ new_job = {
115
+ "job_name": job_name,
116
+ "static_configs": [{"targets": [server_address]}],
117
+ }
118
+ config.setdefault("scrape_configs", []).append(new_job)
119
+ else:
120
+ for job in config["scrape_configs"]:
121
+ if job.get("job_name") == job_name:
122
+ job["static_configs"] = [{"targets": [server_address]}]
123
+
124
+ with open(self.config_file, "w") as f:
125
+ yaml.dump(config, f)
126
+
127
+ info(f"Prometheus configuration updated with target: {server_address}")
128
+
129
+ except Exception as e:
130
+ error(f"Failed to update Prometheus configuration: {e}")
131
+ raise
132
+
133
+ def _is_container_running(self) -> bool:
134
+ """
135
+ Check if a Prometheus container is already running.
136
+
137
+ Returns
138
+ -------
139
+ bool
140
+ True if the Prometheus container is running, False otherwise.
141
+ """
142
+ try:
143
+ container = self.docker.containers.get(self.ctx.prometheus_container_name)
144
+ return container.status == "running"
145
+ except docker.errors.NotFound:
146
+ return False
@@ -0,0 +1,5 @@
1
+ global:
2
+ # How frequently to scrape targets by default.
3
+ scrape_interval: 1m
4
+
5
+ scrape_configs: []