diff --git a/src/ert/config/queue_config.py b/src/ert/config/queue_config.py index 3ac8579173a..e8fc9efb90b 100644 --- a/src/ert/config/queue_config.py +++ b/src/ert/config/queue_config.py @@ -92,7 +92,7 @@ def driver_options(self) -> dict[str, Any]: @pydantic.dataclasses.dataclass class LocalQueueOptions(QueueOptions): - name: Literal[QueueSystem.LOCAL] = QueueSystem.LOCAL + name: Literal[QueueSystem.LOCAL, "local", "LOCAL"] = "local" @property def driver_options(self) -> dict[str, Any]: @@ -101,7 +101,7 @@ def driver_options(self) -> dict[str, Any]: @pydantic.dataclasses.dataclass class LsfQueueOptions(QueueOptions): - name: Literal[QueueSystem.LSF] = QueueSystem.LSF + name: Literal[QueueSystem.LSF, "lsf", "LSF"] = "lsf" bhist_cmd: NonEmptyString | None = None bjobs_cmd: NonEmptyString | None = None bkill_cmd: NonEmptyString | None = None @@ -124,7 +124,7 @@ def driver_options(self) -> dict[str, Any]: @pydantic.dataclasses.dataclass class TorqueQueueOptions(QueueOptions): - name: Literal[QueueSystem.TORQUE] = QueueSystem.TORQUE + name: Literal[QueueSystem.TORQUE, "torque", "TORQUE"] = "torque" qsub_cmd: NonEmptyString | None = None qstat_cmd: NonEmptyString | None = None qdel_cmd: NonEmptyString | None = None @@ -145,7 +145,7 @@ def driver_options(self) -> dict[str, Any]: @pydantic.dataclasses.dataclass class SlurmQueueOptions(QueueOptions): - name: Literal[QueueSystem.SLURM] = QueueSystem.SLURM + name: Literal[QueueSystem.SLURM, "SLURM", "slurm"] = "slurm" sbatch: NonEmptyString = "sbatch" scancel: NonEmptyString = "scancel" scontrol: NonEmptyString = "scontrol" diff --git a/src/everest/config/everest_config.py b/src/everest/config/everest_config.py index 9892dc1937f..0a21bae1ac6 100644 --- a/src/everest/config/everest_config.py +++ b/src/everest/config/everest_config.py @@ -1,6 +1,7 @@ import logging import os from argparse import ArgumentParser +from copy import copy from functools import cached_property from io import StringIO from itertools import chain @@ -215,7 +216,7 @@ class EverestConfig(BaseModelWithPropertySupport): # type: ignore """, ) server: ServerConfig | None = Field( - default=None, + default_factory=ServerConfig, description="""Defines Everest server settings, i.e., which queue system, queue name and queue options are used for the everest server. The main reason for changing this section is situations where everest @@ -250,6 +251,25 @@ class EverestConfig(BaseModelWithPropertySupport): # type: ignore config_path: Path = Field() model_config = ConfigDict(extra="forbid") + @model_validator(mode="after") + def validate_queue_system(self) -> Self: # pylint: disable=E0213 + if self.server is None: + self.server = ServerConfig(queue_system=copy(self.simulator.queue_system)) + elif self.server.queue_system is None: + self.server.queue_system = copy(self.simulator.queue_system) + if ( + str(self.simulator.queue_system.name).lower() == "local" + and str(self.server.queue_system.name).lower() + != str(self.simulator.queue_system.name).lower() + ): + raise ValueError( + f"The simulator is using local as queue system " + f"while the everest server is using {self.server.queue_system.name}. " + f"If the simulator is using local, so must the everest server." + ) + self.server.queue_system.max_running = 1 + return self + @model_validator(mode="after") def validate_forward_model_job_name_installed(self) -> Self: # pylint: disable=E0213 install_jobs = self.install_jobs @@ -745,7 +765,7 @@ def with_defaults(cls, **kwargs): "model": {"realizations": [0]}, } - return EverestConfig.model_validate({**defaults, **kwargs}) + return cls.model_validate({**defaults, **kwargs}) @staticmethod def lint_config_dict(config: dict) -> list["ErrorDetails"]: diff --git a/src/everest/config/has_ert_queue_options.py b/src/everest/config/has_ert_queue_options.py deleted file mode 100644 index 162ddc81b38..00000000000 --- a/src/everest/config/has_ert_queue_options.py +++ /dev/null @@ -1,13 +0,0 @@ -from typing import Any - - -class HasErtQueueOptions: - def extract_ert_queue_options( - self, queue_system: str, everest_to_ert_key_tuples: list[tuple[str, str]] - ) -> list[tuple[str, str, Any]]: - result = [] - for ever_key, ert_key in everest_to_ert_key_tuples: - attribute = getattr(self, ever_key) - if attribute is not None: - result.append((queue_system, ert_key, attribute)) - return result diff --git a/src/everest/config/server_config.py b/src/everest/config/server_config.py index bde3db4e13d..dfdf8f15d87 100644 --- a/src/everest/config/server_config.py +++ b/src/everest/config/server_config.py @@ -1,8 +1,16 @@ import json import os -from typing import Literal +from typing import Any -from pydantic import BaseModel, ConfigDict, Field +from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator + +from ert.config.queue_config import ( + LocalQueueOptions, + LsfQueueOptions, + SlurmQueueOptions, + TorqueQueueOptions, +) +from ert.plugins import ErtPluginManager from ..strings import ( CERTIFICATE_DIR, @@ -11,10 +19,9 @@ SERVER_STATUS, SESSION_DIR, ) -from .has_ert_queue_options import HasErtQueueOptions -class ServerConfig(BaseModel, HasErtQueueOptions): # type: ignore +class ServerConfig(BaseModel): # type: ignore name: str | None = Field( None, description="""Specifies which queue to use. @@ -27,30 +34,47 @@ class ServerConfig(BaseModel, HasErtQueueOptions): # type: ignore as RMS and Eclipse. """, ) # Corresponds to queue name - exclude_host: str | None = Field( - "", - description="""Comma separated list of nodes that should be - excluded from the slurm run""", - ) - include_host: str | None = Field( - "", - description="""Comma separated list of nodes that - should be included in the slurm run""", - ) - options: str | None = Field( - None, - description="""Used to specify options to LSF. - Examples to set memory requirement is: - * rusage[mem=1000]""", - ) - queue_system: Literal["lsf", "local", "slurm"] | None = Field( - None, - description="Defines which queue system the everest server runs on.", + queue_system: ( + LocalQueueOptions + | LsfQueueOptions + | SlurmQueueOptions + | TorqueQueueOptions + | None + ) = Field( + default=None, + description="Defines which queue system the everest submits jobs to", + discriminator="name", ) model_config = ConfigDict( extra="forbid", ) + @field_validator("queue_system", mode="before") + @classmethod + def default_local_queue(cls, v): + if v is None: + return v + elif "activate_script" not in v and ErtPluginManager().activate_script(): + v["activate_script"] = ErtPluginManager().activate_script() + return v + + @model_validator(mode="before") + @classmethod + def check_old_config(cls, data: Any) -> Any: + if isinstance(data, dict): + queue_system = data.get("queue_system") + queue_systems = { + "lsf": LsfQueueOptions, + "torque": TorqueQueueOptions, + "slurm": SlurmQueueOptions, + "local": LocalQueueOptions, + } + if isinstance(queue_system, str) and queue_system in queue_systems: + raise ValueError( + f"Queue system configuration has changed, valid options for {queue_system} are: {list(queue_systems[queue_system].__dataclass_fields__.keys())}" + ) + return data + @staticmethod def get_server_url(output_dir: str) -> str: """Return the url of the server. diff --git a/src/everest/config/simulator_config.py b/src/everest/config/simulator_config.py index 25dd3a4bc32..ed206fa45c7 100644 --- a/src/everest/config/simulator_config.py +++ b/src/everest/config/simulator_config.py @@ -1,26 +1,26 @@ -import warnings -from typing import Literal +from typing import Any -from pydantic import BaseModel, Field, NonNegativeInt, PositiveInt, field_validator +from pydantic import ( + BaseModel, + Field, + NonNegativeInt, + PositiveInt, + field_validator, + model_validator, +) -from .has_ert_queue_options import HasErtQueueOptions +from ert.config.queue_config import ( + LocalQueueOptions, + LsfQueueOptions, + SlurmQueueOptions, + TorqueQueueOptions, +) +from ert.plugins import ErtPluginManager simulator_example = {"queue_system": {"name": "local", "max_running": 3}} -class SimulatorConfig(BaseModel, HasErtQueueOptions, extra="forbid"): # type: ignore - name: str | None = Field(default=None, description="Specifies which queue to use") - cores: PositiveInt | None = Field( - default=None, - description="""Defines the number of simultaneously running forward models. - - When using queue system lsf, this corresponds to number of nodes used at one - time, whereas when using the local queue system, cores refers to the number of - cores you want to use on your system. - - This number is specified in Ert as MAX_RUNNING. - """, - ) +class SimulatorConfig(BaseModel, extra="forbid"): # type: ignore cores_per_node: PositiveInt | None = Field( default=None, description="""defines the number of CPUs when running @@ -35,16 +35,6 @@ class SimulatorConfig(BaseModel, HasErtQueueOptions, extra="forbid"): # type: i description="Whether the batch folder for a successful simulation " "needs to be deleted.", ) - exclude_host: str | None = Field( - "", - description="""Comma separated list of nodes that should be - excluded from the slurm run.""", - ) - include_host: str | None = Field( - "", - description="""Comma separated list of nodes that - should be included in the slurm run""", - ) max_runtime: NonNegativeInt | None = Field( default=None, description="""Maximum allowed running time of a forward model. When @@ -52,15 +42,17 @@ class SimulatorConfig(BaseModel, HasErtQueueOptions, extra="forbid"): # type: i A value of 0 means unlimited runtime. """, ) - options: str | None = Field( + queue_system: ( + LocalQueueOptions + | LsfQueueOptions + | SlurmQueueOptions + | TorqueQueueOptions + | None + ) = Field( default=None, - description="""Used to specify options to LSF. - Examples to set memory requirement is: - * rusage[mem=1000]""", - ) - queue_system: Literal["lsf", "local", "slurm", "torque"] | None = Field( - default="local", - description="Defines which queue system the everest server runs on.", + description="Defines which queue system the everest submits jobs to", + discriminator="name", + validate_default=True, ) resubmit_limit: NonNegativeInt | None = Field( default=None, @@ -73,38 +65,6 @@ class SimulatorConfig(BaseModel, HasErtQueueOptions, extra="forbid"): # type: i resumbit_limit defines the number of times we will resubmit a failing forward model. If not specified, a default value of 1 will be used.""", ) - sbatch: str | None = Field( - default=None, - description="sbatch executable to be used by the slurm queue interface.", - ) - scancel: str | None = Field( - default=None, - description="scancel executable to be used by the slurm queue interface.", - ) - scontrol: str | None = Field( - default=None, - description="scontrol executable to be used by the slurm queue interface.", - ) - sacct: str | None = Field( - default=None, - description="sacct executable to be used by the slurm queue interface.", - ) - squeue: str | None = Field( - default=None, - description="squeue executable to be used by the slurm queue interface.", - ) - server: str | None = Field( - default=None, - description="Name of LSF server to use. This option is deprecated and no longer required", - ) - slurm_timeout: int | None = Field( - default=None, - description="Timeout for cached status used by the slurm queue interface", - ) - squeue_timeout: int | None = Field( - default=None, - description="Timeout for cached status used by the slurm queue interface.", - ) enable_cache: bool = Field( default=False, description="""Enable forward model result caching. @@ -118,32 +78,29 @@ class SimulatorConfig(BaseModel, HasErtQueueOptions, extra="forbid"): # type: i the most common use of a standard optimization with a continuous optimizer.""", ) - qsub_cmd: str | None = Field(default="qsub", description="The submit command") - qstat_cmd: str | None = Field(default="qstat", description="The query command") - qdel_cmd: str | None = Field(default="qdel", description="The kill command") - cluster_label: str | None = Field( - default=None, - description="The name of the cluster you are running simulations in.", - ) - keep_qsub_output: int | None = Field( - default=0, - description="Set to 1 to keep error messages from qsub. Usually only to be used if somethign is seriously wrong with the queue environment/setup.", - ) - submit_sleep: float | None = Field( - default=0.5, - description="To avoid stressing the TORQUE/PBS system you can instruct the driver to sleep for every submit request. The argument to the SUBMIT_SLEEP is the number of seconds to sleep for every submit, which can be a fraction like 0.5", - ) - project_code: str | None = Field( - default=None, - description="String identifier used to map hardware resource usage to a project or account. The project or account does not have to exist.", - ) - @field_validator("server") + @field_validator("queue_system", mode="before") + @classmethod + def default_local_queue(cls, v): + if v is None: + return LocalQueueOptions(max_running=8) + elif "activate_script" not in v and ErtPluginManager().activate_script(): + v["activate_script"] = ErtPluginManager().activate_script() + return v + + @model_validator(mode="before") @classmethod - def validate_server(cls, server): # pylint: disable=E0213 - if server is not None and server: - warnings.warn( - "The simulator server property was deprecated and is no longer needed", - DeprecationWarning, - stacklevel=1, - ) + def check_old_config(cls, data: Any) -> Any: + if isinstance(data, dict): + queue_system = data.get("queue_system") + queue_systems = { + "lsf": LsfQueueOptions, + "torque": TorqueQueueOptions, + "slurm": SlurmQueueOptions, + "local": LocalQueueOptions, + } + if isinstance(queue_system, str) and queue_system in queue_systems: + raise ValueError( + f"Queue system configuration has changed, valid options for {queue_system} are: {list(queue_systems[queue_system].__dataclass_fields__.keys())}" + ) + return data diff --git a/src/everest/detached/__init__.py b/src/everest/detached/__init__.py index e0b210efbae..60a3c1ab96f 100644 --- a/src/everest/detached/__init__.py +++ b/src/everest/detached/__init__.py @@ -15,20 +15,10 @@ from seba_sqlite.exceptions import ObjectNotFoundError from seba_sqlite.snapshot import SebaSnapshot -from ert.config import QueueSystem -from ert.config.queue_config import ( - LocalQueueOptions, - LsfQueueOptions, - QueueOptions, - SlurmQueueOptions, - TorqueQueueOptions, - activate_script, -) -from ert.plugins import ErtPluginManager from ert.scheduler import create_driver from ert.scheduler.driver import Driver, FailedSubmit from ert.scheduler.event import StartedEvent -from everest.config import EverestConfig, ServerConfig, SimulatorConfig +from everest.config import EverestConfig, ServerConfig from everest.config_keys import ConfigKeys as CK from everest.strings import ( EVEREST_SERVER_CONFIG, @@ -56,7 +46,7 @@ async def start_server(config: EverestConfig, debug: bool = False) -> Driver: """ Start an Everest server running the optimization defined in the config """ - driver = create_driver(get_server_queue_options(config.simulator, config.server)) + driver = create_driver(config.server.queue_system) try: args = ["--config-file", str(config.config_path)] if debug: @@ -259,63 +249,6 @@ def start_monitor( } -def _find_res_queue_system( - simulator: SimulatorConfig | None, - server: ServerConfig | None, -): - queue_system_simulator: Literal["lsf", "local", "slurm", "torque"] = "local" - if simulator is not None and simulator.queue_system is not None: - queue_system_simulator = simulator.queue_system - - queue_system = queue_system_simulator - if server is not None: - queue_system = server.queue_system or queue_system - - if queue_system_simulator == CK.LOCAL and queue_system_simulator != queue_system: - raise ValueError( - f"The simulator is using {CK.LOCAL} as queue system " - f"while the everest server is using {queue_system}. " - f"If the simulator is using {CK.LOCAL}, so must the everest server." - ) - - assert queue_system is not None - return QueueSystem(queue_system.upper()) - - -def get_server_queue_options( - simulator: SimulatorConfig | None, - server: ServerConfig | None, -) -> QueueOptions: - script = ErtPluginManager().activate_script() or activate_script() - queue_system = _find_res_queue_system(simulator, server) - ever_queue_config = server if server is not None else simulator - if queue_system == QueueSystem.LSF: - queue = LsfQueueOptions( - activate_script=script, - lsf_queue=ever_queue_config.name, - lsf_resource=ever_queue_config.options, - ) - elif queue_system == QueueSystem.SLURM: - queue = SlurmQueueOptions( - activate_script=script, - exclude_host=ever_queue_config.exclude_host, - include_host=ever_queue_config.include_host, - partition=ever_queue_config.name, - ) - elif queue_system == QueueSystem.TORQUE: - queue = TorqueQueueOptions( - activate_script=script, - queue=ever_queue_config.name, - keep_qsub_output=ever_queue_config.keep_qsub_output, - ) - elif queue_system == QueueSystem.LOCAL: - queue = LocalQueueOptions() - else: - raise ValueError(f"Unknown queue system: {queue_system}") - queue.max_running = 1 - return queue - - def _query_server(cert, auth, endpoint): """Retrieve data from an endpoint as a dictionary""" response = requests.get(endpoint, verify=cert, auth=auth, proxies=PROXY) diff --git a/src/everest/queue_driver/__init__.py b/src/everest/queue_driver/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/everest/queue_driver/queue_driver.py b/src/everest/queue_driver/queue_driver.py deleted file mode 100644 index d33f7601b7d..00000000000 --- a/src/everest/queue_driver/queue_driver.py +++ /dev/null @@ -1,83 +0,0 @@ -from typing import Any - -from ert.config import QueueSystem -from everest.config import EverestConfig -from everest.config.simulator_config import SimulatorConfig -from everest.config_keys import ConfigKeys - -_LSF_OPTIONS = [ - (ConfigKeys.CORES, "MAX_RUNNING"), - (ConfigKeys.LSF_QUEUE_NAME, "LSF_QUEUE"), - (ConfigKeys.LSF_OPTIONS, "LSF_RESOURCE"), -] - -_SLURM_OPTIONS = [ - (ConfigKeys.CORES, "MAX_RUNNING"), - (ConfigKeys.SLURM_QUEUE, "PARTITION"), - (ConfigKeys.SLURM_SBATCH, "SBATCH"), - (ConfigKeys.SLURM_SCANCEL, "SCANCEL"), - (ConfigKeys.SLURM_SCONTROL, "SCONTROL"), - (ConfigKeys.SLURM_SACCT, "SACCT"), - (ConfigKeys.SLURM_SQUEUE, "SQUEUE"), - (ConfigKeys.SLURM_MAX_RUNTIME, "MAX_RUNTIME"), - (ConfigKeys.SLURM_SQUEUE_TIMEOUT, "SQUEUE_TIMEOUT"), - (ConfigKeys.SLURM_EXCLUDE_HOST_OPTION, "EXCLUDE_HOST"), - (ConfigKeys.SLURM_INCLUDE_HOST_OPTION, "INCLUDE_HOST"), -] - -_TORQUE_OPTIONS = [ - (ConfigKeys.CORES, "MAX_RUNNING"), - (ConfigKeys.TORQUE_QSUB_CMD, "QSUB_CMD"), - (ConfigKeys.TORQUE_QSTAT_CMD, "QSTAT_CMD"), - (ConfigKeys.TORQUE_QDEL_CMD, "QDEL_CMD"), - (ConfigKeys.TORQUE_QUEUE_NAME, "QUEUE"), - (ConfigKeys.TORQUE_CLUSTER_LABEL, "CLUSTER_LABEL"), - (ConfigKeys.TORQUE_KEEP_QSUB_OUTPUT, "KEEP_QSUB_OUTPUT"), - (ConfigKeys.TORQUE_SUBMIT_SLEEP, "SUBMIT_SLEEP"), - (ConfigKeys.TORQUE_PROJECT_CODE, "PROJECT_CODE"), -] - - -def _extract_ert_queue_options_from_simulator_config( - simulator: SimulatorConfig | None, queue_system -) -> list[tuple[str, str, Any]]: - if simulator is None: - simulator = SimulatorConfig() - - if queue_system == ConfigKeys.LSF: - return simulator.extract_ert_queue_options( - queue_system=QueueSystem.LSF, everest_to_ert_key_tuples=_LSF_OPTIONS - ) - elif queue_system == ConfigKeys.LOCAL: - return [ - ( - QueueSystem.LOCAL, - "MAX_RUNNING", - simulator.cores or 8, - ) - ] - elif queue_system == ConfigKeys.TORQUE: - return simulator.extract_ert_queue_options( - queue_system=QueueSystem.TORQUE, everest_to_ert_key_tuples=_TORQUE_OPTIONS - ) - elif queue_system == ConfigKeys.SLURM: - return simulator.extract_ert_queue_options( - queue_system=QueueSystem.SLURM, everest_to_ert_key_tuples=_SLURM_OPTIONS - ) - - raise KeyError( - f"Invalid queue_system: {queue_system}, " - "expected one of: ['lsf', 'local', 'slurm', 'torque']" - ) - - -def _extract_queue_system(ever_config: EverestConfig, ert_config): - queue_system = ( - ever_config.simulator.queue_system if ever_config.simulator else None - ) or "local" - ert_config["QUEUE_SYSTEM"] = QueueSystem(queue_system.upper()) - ert_config.setdefault("QUEUE_OPTION", []).extend( - _extract_ert_queue_options_from_simulator_config( - ever_config.simulator, queue_system - ) - ) diff --git a/src/everest/simulator/everest_to_ert.py b/src/everest/simulator/everest_to_ert.py index 9af43c8d6b1..38f85d646e0 100644 --- a/src/everest/simulator/everest_to_ert.py +++ b/src/everest/simulator/everest_to_ert.py @@ -18,7 +18,6 @@ from everest.config.install_job_config import InstallJobConfig from everest.config.simulator_config import SimulatorConfig from everest.config_keys import ConfigKeys -from everest.queue_driver.queue_driver import _extract_queue_system from everest.strings import EVEREST, SIMULATION_DIR, STORAGE_DIR @@ -474,7 +473,6 @@ def _everest_to_ert_config_dict( _extract_workflow_jobs(ever_config, ert_config, config_dir) _extract_workflows(ever_config, ert_config, config_dir) _extract_model(ever_config, ert_config) - _extract_queue_system(ever_config, ert_config) _extract_seed(ever_config, ert_config) _extract_results(ever_config, ert_config) @@ -487,6 +485,8 @@ def everest_to_ert_config(ever_config: EverestConfig) -> ErtConfig: ever_config, site_config=ErtConfig.read_site_config() ) ert_config = ErtConfig.with_plugins().from_dict(config_dict=config_dict) + ert_config.queue_config.queue_options = ever_config.simulator.queue_system + ert_config.queue_config.queue_system = ever_config.simulator.queue_system.name ens_config = ert_config.ensemble_config def _get_variables( diff --git a/test-data/everest/egg/everest/model/config.yml b/test-data/everest/egg/everest/model/config.yml index 795af7f8e93..ac4049ff15a 100644 --- a/test-data/everest/egg/everest/model/config.yml +++ b/test-data/everest/egg/everest/model/config.yml @@ -96,8 +96,9 @@ environment: random_seed: 123456 simulator: - queue_system: local - cores: 3 + queue_system: + name: local + max_running: 3 install_data: - diff --git a/test-data/everest/egg/everest/model/config_flow.yml b/test-data/everest/egg/everest/model/config_flow.yml index d1dcbf1dc29..90223138436 100644 --- a/test-data/everest/egg/everest/model/config_flow.yml +++ b/test-data/everest/egg/everest/model/config_flow.yml @@ -94,8 +94,9 @@ environment: random_seed: 123456 simulator: - queue_system: local - cores: 3 + queue_system: + name: local + max_running: 3 install_data: - diff --git a/tests/ert/unit_tests/config/test_queue_config.py b/tests/ert/unit_tests/config/test_queue_config.py index 9f2105182a8..73c1d2dc3f5 100644 --- a/tests/ert/unit_tests/config/test_queue_config.py +++ b/tests/ert/unit_tests/config/test_queue_config.py @@ -402,7 +402,7 @@ def test_default_activate_script_generation(expected, monkeypatch, venv): monkeypatch.setenv("VIRTUAL_ENV", venv) else: monkeypatch.delenv("VIRTUAL_ENV", raising=False) - options = QueueOptions(name="local") + options = LocalQueueOptions() assert options.activate_script == expected diff --git a/tests/everest/test_config_validation.py b/tests/everest/test_config_validation.py index 67acc1d1a3b..a44c6f4243e 100644 --- a/tests/everest/test_config_validation.py +++ b/tests/everest/test_config_validation.py @@ -3,6 +3,7 @@ import re import warnings from argparse import ArgumentParser +from contextlib import ExitStack as does_not_raise from pathlib import Path from typing import Any from unittest.mock import patch @@ -144,34 +145,37 @@ def test_that_max_runtime_errors_only_on_negative(): def test_that_invalid_queue_system_errors(): - with pytest.raises(ValueError) as e: - EverestConfig.with_defaults(simulator={"queue_system": "docal"}) + with pytest.raises( + ValueError, match=r"does not match .*'local',.*'lsf',.*'slurm', .*'torque'" + ): + EverestConfig.with_defaults(simulator={"queue_system": {"name": "docal"}}) - assert has_error( - e.value, match="Input should be 'lsf', 'local', 'slurm' or 'torque'" + +@pytest.mark.parametrize( + ["cores", "expected_error"], [(0, False), (-1, True), (1, False)] +) +def test_that_cores_errors_only_on_lt_eq0(cores, expected_error): + expectation = ( + pytest.raises(ValueError, match="greater than or equal to 0") + if expected_error + else does_not_raise() ) - EverestConfig.with_defaults(simulator={"queue_system": "local"}) - EverestConfig.with_defaults(simulator={"queue_system": "lsf"}) - EverestConfig.with_defaults(simulator={"queue_system": "slurm"}) - EverestConfig.with_defaults(simulator={"queue_system": "torque"}) + with expectation: + EverestConfig.with_defaults( + simulator={"queue_system": {"name": "local", "max_running": cores}} + ) @pytest.mark.parametrize( ["cores", "expected_error"], [(0, True), (-1, True), (1, False)] ) -def test_that_cores_errors_only_on_lt0(cores, expected_error): - if expected_error: - with pytest.raises(ValueError) as e: - EverestConfig.with_defaults(simulator={"cores": cores}) - - assert has_error(e.value, match=".*greater than 0") - - with pytest.raises(ValueError) as e: - EverestConfig.with_defaults(simulator={"cores_per_node": cores}) - - assert has_error(e.value, match=".*greater than 0") - else: - EverestConfig.with_defaults(simulator={"cores": cores}) +def test_that_cores_per_node_errors_only_on_lt0(cores, expected_error): + expectation = ( + pytest.raises(ValueError, match="greater than 0") + if expected_error + else does_not_raise() + ) + with expectation: EverestConfig.with_defaults(simulator={"cores_per_node": cores}) diff --git a/tests/everest/test_detached.py b/tests/everest/test_detached.py index 63de5a744dc..ba7fb3eafc7 100644 --- a/tests/everest/test_detached.py +++ b/tests/everest/test_detached.py @@ -1,19 +1,17 @@ import os import stat from pathlib import Path -from unittest.mock import MagicMock, patch +from unittest.mock import patch import pytest import requests -import everest.detached from ert.config import ErtConfig from ert.config.queue_config import ( LocalQueueOptions, LsfQueueOptions, SlurmQueueOptions, TorqueQueueOptions, - activate_script, ) from ert.scheduler.event import FinishedEvent from everest.config import EverestConfig, InstallJobConfig @@ -24,9 +22,7 @@ _EVERSERVER_JOB_PATH, PROXY, ServerStatus, - _find_res_queue_system, everserver_status, - get_server_queue_options, server_is_running, start_server, stop_server, @@ -34,7 +30,6 @@ wait_for_server, wait_for_server_to_stop, ) -from everest.simulator.everest_to_ert import _everest_to_ert_config_dict from everest.strings import ( DEFAULT_OUTPUT_DIR, DETACHED_NODE_DIR, @@ -191,129 +186,94 @@ def _get_reference_config(): def test_detached_mode_config_base(copy_math_func_test_data_to_tmp): everest_config, _ = _get_reference_config() - queue_config = get_server_queue_options( - everest_config.simulator, everest_config.server - ) - - assert queue_config == LocalQueueOptions(max_running=1) + assert everest_config.simulator.queue_system == LocalQueueOptions(max_running=8) @pytest.mark.parametrize( - "queue_system, cores, name", + "queue_system, cores", [ - ("lsf", 2, None), - ("slurm", 4, None), - ("torque", 6, None), - ("lsf", 3, "test_lsf"), - ("slurm", 5, "test_slurm"), - ("torque", 7, "test_torque"), + ("lsf", 2), + ("slurm", 4), + ("lsf", 3), + ("slurm", 5), + ("torque", 7), ], ) -def test_everserver_queue_config_equal_to_run_config( - copy_math_func_test_data_to_tmp, queue_system, cores, name -): - everest_config, _ = _get_reference_config() - - simulator_config = {CK.QUEUE_SYSTEM: queue_system, CK.CORES: cores} +def test_everserver_queue_config_equal_to_run_config(queue_system, cores): + simulator_config = {CK.QUEUE_SYSTEM: {"name": queue_system, "max_running": cores}} + everest_config = EverestConfig.with_defaults(**{"simulator": simulator_config}) + everest_config.server.queue_system = SimulatorConfig(**simulator_config) - if name is not None: - simulator_config.update({"name": name}) - everest_config.simulator = SimulatorConfig(**simulator_config) - server_queue_option = get_server_queue_options( - everest_config.simulator, everest_config.server - ) - ert_config = _everest_to_ert_config_dict(everest_config) - - run_queue_option = ert_config["QUEUE_OPTION"] - - assert ert_config["QUEUE_SYSTEM"] == server_queue_option.name - assert ( - next(filter(lambda x: "MAX_RUNNING" in x, reversed(run_queue_option)))[-1] - == cores - ) - assert server_queue_option.max_running == 1 - if name is not None: - option = next(filter(lambda x: name in x, run_queue_option)) - assert option[-1] == name == getattr(server_queue_option, option[1].lower()) - - -@pytest.mark.parametrize("queue_system", ["lsf", "slurm"]) -def test_detached_mode_config_only_sim(copy_math_func_test_data_to_tmp, queue_system): - everest_config, reference = _get_reference_config() - - reference["QUEUE_SYSTEM"] = queue_system.upper() - queue_options = [(queue_system.upper(), "MAX_RUNNING", 1)] - reference.setdefault("QUEUE_OPTION", []).extend(queue_options) - everest_config.simulator = SimulatorConfig(**{CK.QUEUE_SYSTEM: queue_system}) - queue_config = get_server_queue_options( - everest_config.simulator, everest_config.server - ) - assert str(queue_config.name.name).lower() == queue_system - -def test_detached_mode_config_error(copy_math_func_test_data_to_tmp): +def test_detached_mode_config_error(): """ We are not allowing the simulator queue to be local and at the same time the everserver queue to be something other than local """ - everest_config, _ = _get_reference_config() - - everest_config.server = ServerConfig(name="server", queue_system="lsf") with pytest.raises(ValueError, match="so must the everest server"): - get_server_queue_options(everest_config.simulator, everest_config.server) + EverestConfig.with_defaults( + **{ + "simulator": {CK.QUEUE_SYSTEM: {"name": "local"}}, + "server": {CK.QUEUE_SYSTEM: {"name": "lsf"}}, + } + ) @pytest.mark.parametrize( "config, expected_result", [ ( - EverestConfig.with_defaults(**{CK.SIMULATOR: {CK.QUEUE_SYSTEM: "lsf"}}), - "LSF", + EverestConfig.with_defaults( + **{CK.SIMULATOR: {CK.QUEUE_SYSTEM: {"name": "lsf"}}} + ), + "lsf", ), ( EverestConfig.with_defaults( **{ - CK.SIMULATOR: {CK.QUEUE_SYSTEM: "lsf"}, - CK.EVERSERVER: {CK.QUEUE_SYSTEM: "lsf"}, + CK.SIMULATOR: {CK.QUEUE_SYSTEM: {"name": "lsf"}}, + CK.EVERSERVER: {CK.QUEUE_SYSTEM: {"name": "lsf"}}, } ), - "LSF", + "lsf", ), - (EverestConfig.with_defaults(**{}), "LOCAL"), + (EverestConfig.with_defaults(**{}), "local"), ( - EverestConfig.with_defaults(**{CK.SIMULATOR: {CK.QUEUE_SYSTEM: "local"}}), - "LOCAL", + EverestConfig.with_defaults( + **{CK.SIMULATOR: {CK.QUEUE_SYSTEM: {"name": "local"}}} + ), + "local", ), ], ) def test_find_queue_system(config: EverestConfig, expected_result): - result = _find_res_queue_system(config.simulator, config.server) + result = config.simulator - assert result == expected_result + assert result.queue_system.name == expected_result def test_generate_queue_options_no_config(): config = EverestConfig.with_defaults(**{}) - assert get_server_queue_options( - config.simulator, config.server - ) == LocalQueueOptions(max_running=1) + assert config.server.queue_system == LocalQueueOptions(max_running=1) @pytest.mark.parametrize( "queue_options, expected_result", [ ( - {"options": "ever_opt_1", "queue_system": "slurm"}, - SlurmQueueOptions(max_running=1), + {"partition": "ever_opt_1", "name": "slurm"}, + SlurmQueueOptions(max_running=1, partition="ever_opt_1"), ), ( - {"options": "ever_opt_1", "queue_system": "lsf"}, - LsfQueueOptions(max_running=1, lsf_resource="ever_opt_1"), + {"lsf_queue": "ever_opt_1", "name": "lsf"}, + LsfQueueOptions( + max_running=1, + lsf_queue="ever_opt_1", + ), ), ( { - "options": "ever_opt_1", - "queue_system": "torque", + "name": "torque", "keep_qsub_output": "1", }, TorqueQueueOptions(max_running=1, keep_qsub_output=True), @@ -323,13 +283,10 @@ def test_generate_queue_options_no_config(): def test_generate_queue_options_use_simulator_values( queue_options, expected_result, monkeypatch ): - monkeypatch.setattr( - everest.detached.ErtPluginManager, - "activate_script", - MagicMock(return_value=activate_script()), + config = EverestConfig.with_defaults( + **{"simulator": {"queue_system": queue_options}} ) - config = EverestConfig.with_defaults(**{"simulator": queue_options}) - assert get_server_queue_options(config.simulator, config.server) == expected_result + assert config.server.queue_system == expected_result @pytest.mark.timeout(5) # Simulation might not finish diff --git a/tests/everest/test_egg_simulation.py b/tests/everest/test_egg_simulation.py index 8494c9d52b1..282f1e75615 100644 --- a/tests/everest/test_egg_simulation.py +++ b/tests/everest/test_egg_simulation.py @@ -4,7 +4,7 @@ import pytest -from ert.config import ErtConfig, QueueSystem +from ert.config import ErtConfig from ert.config.parsing import ConfigKeys as ErtConfigKeys from ert.ensemble_evaluator import EvaluatorServerConfig from ert.run_models.everest_run_model import EverestRunModel @@ -470,8 +470,6 @@ def _generate_exp_ert_config(config_path, output_dir): return { ErtConfigKeys.DEFINE: [("", config_path)], ErtConfigKeys.INSTALL_JOB: everest_default_jobs(output_dir), - ErtConfigKeys.QUEUE_OPTION: [(QueueSystem.LOCAL, "MAX_RUNNING", 3)], - ErtConfigKeys.QUEUE_SYSTEM: QueueSystem.LOCAL, ErtConfigKeys.NUM_REALIZATIONS: NUM_REALIZATIONS, ErtConfigKeys.RUNPATH: os.path.join( output_dir, diff --git a/tests/everest/test_everest_config.py b/tests/everest/test_everest_config.py index ca55cb01dbb..cf27551ae63 100644 --- a/tests/everest/test_everest_config.py +++ b/tests/everest/test_everest_config.py @@ -3,7 +3,7 @@ import pytest -from everest.config import EverestConfig +from everest.config import EverestConfig, ServerConfig, SimulatorConfig from everest.config.control_config import ControlConfig from everest.config.control_variable_config import ControlVariableConfig from everest.config.cvar_config import CVaRConfig @@ -287,16 +287,9 @@ def test_that_log_level_property_is_consistent_with_environment_log_level(): assert config.logging_level == lvl_int -@pytest.mark.parametrize("server", ["something", "", None]) -def test_deprecation_warning_for_simulator_server(server): - config_src = {"simulator": {"server": server}} - - if not server: - config = EverestConfig.with_defaults(**config_src) - else: - with pytest.deprecated_call( - match="The simulator server property was deprecated" - ): - config = EverestConfig.with_defaults(**config_src) - - assert config.simulator.server is None +@pytest.mark.parametrize("config_class", [SimulatorConfig, ServerConfig]) +@pytest.mark.parametrize("queue_system", ["lsf", "torque", "slurm", "local"]) +def test_removed_queue_options_init(queue_system, config_class): + config = {"queue_system": queue_system} + with pytest.raises(ValueError, match=f"valid options for {queue_system} are"): + config_class(**config) diff --git a/tests/everest/test_math_func.py b/tests/everest/test_math_func.py index 301789eceb2..c5de0df03f9 100644 --- a/tests/everest/test_math_func.py +++ b/tests/everest/test_math_func.py @@ -219,7 +219,7 @@ def test_remove_run_path( # Manually rolling the output folder between two runs makedirs_if_needed(config.output_dir, roll_if_exists=True) - config.simulator = None + config.simulator.delete_run_path = False run_model = EverestRunModel.create(config) evaluator_server_config = evaluator_server_config_generator(run_model) run_model.run_experiment(evaluator_server_config) diff --git a/tests/everest/test_queue_driver.py b/tests/everest/test_queue_driver.py deleted file mode 100644 index 940e883c886..00000000000 --- a/tests/everest/test_queue_driver.py +++ /dev/null @@ -1,40 +0,0 @@ -from unittest.mock import Mock - -import pytest - -from everest.config import EverestConfig -from everest.queue_driver import queue_driver -from everest.queue_driver.queue_driver import _extract_queue_system - - -@pytest.mark.parametrize( - "input_config,queue_system,expected_result", - [ - ( - EverestConfig.with_defaults(**{}), - "local", - {"QUEUE_SYSTEM": "LOCAL", "QUEUE_OPTION": []}, - ), - ( - EverestConfig.with_defaults(**{"simulator": {"queue_system": "lsf"}}), - "lsf", - {"QUEUE_SYSTEM": "LSF", "QUEUE_OPTION": []}, - ), - ( - EverestConfig.with_defaults(**{"simulator": {"queue_system": "slurm"}}), - "slurm", - {"QUEUE_SYSTEM": "SLURM", "QUEUE_OPTION": []}, - ), - ], -) -def test_extract_queue_system(monkeypatch, input_config, queue_system, expected_result): - extract_options_mock = Mock(return_value=[]) - monkeypatch.setattr( - queue_driver, - "_extract_ert_queue_options_from_simulator_config", - extract_options_mock, - ) - ert_config = {} - _extract_queue_system(input_config, ert_config) - assert ert_config == expected_result - extract_options_mock.assert_called_once_with(input_config.simulator, queue_system) diff --git a/tests/everest/test_res_initialization.py b/tests/everest/test_res_initialization.py index a1279fe453a..0a42edbe3e6 100644 --- a/tests/everest/test_res_initialization.py +++ b/tests/everest/test_res_initialization.py @@ -10,6 +10,12 @@ import everest from ert.config import ExtParamConfig from ert.config.parsing import ConfigKeys as ErtConfigKeys +from ert.config.queue_config import ( + LocalQueueOptions, + LsfQueueOptions, + SlurmQueueOptions, + TorqueQueueOptions, +) from everest import ConfigKeys as CK from everest.config import EverestConfig, EverestValidationError from everest.simulator.everest_to_ert import ( @@ -24,80 +30,77 @@ @pytest.mark.parametrize( - "config, expected", + "config, config_class", [ [ { - "queue_system": "torque", - "name": "permanent_8", - "qsub_cmd": "qsub", - "qstat_cmd": "qstat", - "qdel_cmd": "qdel", - "keep_qsub_output": 1, - "submit_sleep": 0.5, - "project_code": "snake_oil_pc", - "cores_per_node": 3, + "name": "local", + "max_running": 0, + "submit_sleep": 0.0, + "project_code": "", + "activate_script": "activate_script", }, + LocalQueueOptions, + ], + [ { - "project_code": "snake_oil_pc", + "name": "torque", "qsub_cmd": "qsub", "qstat_cmd": "qstat", "qdel_cmd": "qdel", - "keep_qsub_output": True, - "queue_name": "permanent_8", + "queue": "queue", + "cluster_label": "cluster_label", + "job_prefix": "job_prefix", + "keep_qsub_output": False, }, + TorqueQueueOptions, ], [ { - "queue_system": "slurm", - "name": "default-queue", - "exclude_host": "host1,host2,host3,host4", - "include_host": "host5,host6,host7,host8", - }, - { - "exclude_hosts": "host1,host2,host3,host4", - "include_hosts": "host5,host6,host7,host8", - "queue_name": "default-queue", - "sacct_cmd": "sacct", - "sbatch_cmd": "sbatch", - "scancel_cmd": "scancel", - "scontrol_cmd": "scontrol", - "squeue_cmd": "squeue", - "squeue_timeout": 2, + "name": "slurm", + "sbatch": "sbatch", + "scancel": "scancel", + "scontrol": "scontrol", + "sacct": "sacct", + "squeue": "squeue", + "exclude_host": "exclude_host", + "include_host": "include_host", + "partition": "some_partition", + "squeue_timeout": 2.0, + "max_runtime": 10, }, + SlurmQueueOptions, ], [ { - "queue_system": "lsf", - "name": "mr", - "options": "span = 1 && select[x86 and GNU/Linux]", - "server": "lx-fastserver01", - }, - { - "queue_name": "mr", - "resource_requirement": "span = 1 && select[x86 and GNU/Linux]", + "name": "lsf", + "bhist_cmd": "bhist", + "bjobs_cmd": "bjobs", + "bkill_cmd": "bkill", + "bsub_cmd": "bsub", + "exclude_host": "", + "lsf_queue": "lsf_queue", + "lsf_resource": "", }, + LsfQueueOptions, ], ], ) -def test_everest_to_ert_queue_config(config, expected): - general_options = {"resubmit_limit": 7, "cores": 42} +def test_everest_to_ert_queue_config(config, config_class): + """Note that these objects are used directly in the Everest + config, and if you have to make changes to this test, it is likely + that it is a breaking change to Everest""" + general_queue_options = {"max_running": 10} + general_options = {"resubmit_limit": 7} + config |= general_queue_options ever_config = EverestConfig.with_defaults( **{ - "simulator": config | general_options, + "simulator": {"queue_system": config} | general_options, "model": {"realizations": [0]}, } ) ert_config = everest_to_ert_config(ever_config) - - qc = ert_config.queue_config - qo = qc.queue_options - assert qc.queue_system == config["queue_system"].upper() - driver_options = qo.driver_options - driver_options.pop("activate_script") - assert {k: v for k, v in driver_options.items() if v is not None} == expected - assert qc.max_submit == general_options["resubmit_limit"] + 1 - assert qo.max_running == general_options["cores"] + assert ert_config.queue_config.queue_options == config_class(**config) def test_everest_to_ert_controls(): diff --git a/tests/everest/test_yaml_parser.py b/tests/everest/test_yaml_parser.py index f918644d71b..aa5dc33074d 100644 --- a/tests/everest/test_yaml_parser.py +++ b/tests/everest/test_yaml_parser.py @@ -54,6 +54,7 @@ def test_read_file(tmp_path, monkeypatch): ConfigKeys.MODEL, ConfigKeys.SIMULATOR, ConfigKeys.OPTIMIZATION, + ConfigKeys.EVERSERVER, ConfigKeys.DEFINITIONS, ConfigKeys.CONFIGPATH, ]