diff --git a/src/ert/config/queue_config.py b/src/ert/config/queue_config.py index 46b13f13aa9..75edeac949e 100644 --- a/src/ert/config/queue_config.py +++ b/src/ert/config/queue_config.py @@ -93,7 +93,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" @property def driver_options(self) -> dict[str, Any]: @@ -102,7 +102,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" bhist_cmd: NonEmptyString | None = None bjobs_cmd: NonEmptyString | None = None bkill_cmd: NonEmptyString | None = None @@ -125,7 +125,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" qsub_cmd: NonEmptyString | None = None qstat_cmd: NonEmptyString | None = None qdel_cmd: NonEmptyString | None = None @@ -146,7 +146,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" sbatch: NonEmptyString = "sbatch" scancel: NonEmptyString = "scancel" scontrol: NonEmptyString = "scontrol" @@ -258,7 +258,6 @@ class QueueConfig: queue_options: ( LsfQueueOptions | TorqueQueueOptions | SlurmQueueOptions | LocalQueueOptions ) = pydantic.Field(default_factory=LocalQueueOptions, discriminator="name") - queue_options_test_run: LocalQueueOptions = field(default_factory=LocalQueueOptions) stop_long_running: bool = False max_runtime: int | None = None preferred_num_cpu: int = 1 @@ -311,7 +310,6 @@ def from_dict(cls, config_dict: ConfigDict) -> QueueConfig: ) queue_options = all_validated_queue_options[selected_queue_system] - queue_options_test_run = all_validated_queue_options[QueueSystem.LOCAL] queue_options.add_global_queue_options(config_dict) if queue_options.project_code is None: @@ -329,7 +327,6 @@ def from_dict(cls, config_dict: ConfigDict) -> QueueConfig: max_submit, selected_queue_system, queue_options, - queue_options_test_run, stop_long_running=bool(stop_long_running), max_runtime=config_dict.get(ConfigKeys.MAX_RUNTIME), preferred_num_cpu=preferred_num_cpu, @@ -341,8 +338,7 @@ def create_local_copy(self) -> QueueConfig: self.realization_memory, self.max_submit, QueueSystem.LOCAL, - self.queue_options_test_run, - self.queue_options_test_run, + LocalQueueOptions(max_running=self.max_running), stop_long_running=bool(self.stop_long_running), max_runtime=self.max_runtime, ) diff --git a/src/ert/gui/simulation/experiment_panel.py b/src/ert/gui/simulation/experiment_panel.py index b454d3448a8..7be4a058423 100644 --- a/src/ert/gui/simulation/experiment_panel.py +++ b/src/ert/gui/simulation/experiment_panel.py @@ -35,6 +35,7 @@ ) from ert.trace import get_trace_id +from ...config.queue_config import LocalQueueOptions from ..summarypanel import SummaryPanel from .combobox_with_description import QComboBoxWithDescription from .ensemble_experiment_panel import EnsembleExperimentPanel @@ -376,7 +377,7 @@ def populate_clipboard_debug_info(self) -> None: queue_opts = self.config.queue_config.queue_options if isinstance(self.get_current_experiment_type(), SingleTestRun): - queue_opts = self.config.queue_config.queue_options_test_run + queue_opts = LocalQueueOptions(max_running=1) for field in fields(queue_opts): field_value = getattr(queue_opts, field.name) diff --git a/src/ert/scheduler/__init__.py b/src/ert/scheduler/__init__.py index 349e1d3838a..bdcf018f109 100644 --- a/src/ert/scheduler/__init__.py +++ b/src/ert/scheduler/__init__.py @@ -19,23 +19,23 @@ def create_driver(queue_options: QueueOptions) -> Driver: - if queue_options.name == QueueSystem.LOCAL: - return LocalDriver() - elif queue_options.name == QueueSystem.TORQUE: - return OpenPBSDriver(**queue_options.driver_options) - elif queue_options.name == QueueSystem.LSF: - return LsfDriver(**queue_options.driver_options) - elif queue_options.name == QueueSystem.SLURM: - return SlurmDriver( - **dict( - {"user": getpwuid(getuid()).pw_name}, - **queue_options.driver_options, + match str(queue_options.name).upper(): + case QueueSystem.LOCAL: + return LocalDriver() + case QueueSystem.TORQUE: + return OpenPBSDriver(**queue_options.driver_options) + case QueueSystem.LSF: + return LsfDriver(**queue_options.driver_options) + case QueueSystem.SLURM: + return SlurmDriver( + **dict( + {"user": getpwuid(getuid()).pw_name}, + **queue_options.driver_options, + ) ) - ) - else: - raise NotImplementedError( - "Only LOCAL, SLURM, TORQUE and LSF drivers are implemented" - ) + raise NotImplementedError( + "Only LOCAL, SLURM, TORQUE and LSF drivers are implemented" + ) __all__ = [ diff --git a/src/everest/config/everest_config.py b/src/everest/config/everest_config.py index 451f0d46d58..f41f00fb736 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..f4f7bd9b27a 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,46 +19,41 @@ SERVER_STATUS, SESSION_DIR, ) -from .has_ert_queue_options import HasErtQueueOptions - - -class ServerConfig(BaseModel, HasErtQueueOptions): # type: ignore - name: str | None = Field( - None, - description="""Specifies which queue to use. - -Examples are -* mr -* bigmem - -The everest server generally has lower resource requirements than forward models such -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.", +from .simulator_config import check_removed_config + + +class ServerConfig(BaseModel): # type: ignore + 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): + check_removed_config(data.get("queue_system")) + 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..4991bc71bdb 100644 --- a/src/everest/config/simulator_config.py +++ b/src/everest/config/simulator_config.py @@ -1,26 +1,39 @@ -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. +def check_removed_config(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())}" + ) - 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 +48,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 +55,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 +78,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 +91,19 @@ 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): + check_removed_config(data.get("queue_system")) + 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/detached/jobs/everserver.py b/src/everest/detached/jobs/everserver.py index 33f5a96c8b8..3422748c5ff 100755 --- a/src/everest/detached/jobs/everserver.py +++ b/src/everest/detached/jobs/everserver.py @@ -31,7 +31,6 @@ HTTPBasicCredentials, ) -from ert.config import QueueSystem from ert.ensemble_evaluator import EvaluatorServerConfig from ert.run_models.everest_run_model import EverestExitCode, EverestRunModel from everest import export_to_csv, export_with_progress @@ -305,7 +304,7 @@ def main(): simulation_callback=partial(_sim_monitor, shared_data=shared_data), optimization_callback=partial(_opt_monitor, shared_data=shared_data), ) - if run_model.ert_config.queue_config.queue_system == QueueSystem.LOCAL: + if config.simulator.queue_system.name == "local": evaluator_server_config = EvaluatorServerConfig() else: evaluator_server_config = EvaluatorServerConfig( 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 b1ac72bc962..ebabb0b7972 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 @@ -485,7 +484,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) @@ -498,6 +496,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 51ae054c267..eba12b1e4be 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/ert/unit_tests/test_run_path_creation.py b/tests/ert/unit_tests/test_run_path_creation.py index 9e70ed4734e..356bd8c077a 100644 --- a/tests/ert/unit_tests/test_run_path_creation.py +++ b/tests/ert/unit_tests/test_run_path_creation.py @@ -94,9 +94,9 @@ def test_jobs_json_is_backed_up(make_run_path): assert os.path.exists("simulations/realization-0/iter-0/jobs.json") make_run_path(ert_config) iter0_output_files = os.listdir("simulations/realization-0/iter-0/") - assert ( - len([f for f in iter0_output_files if f.startswith("jobs.json")]) > 1 - ), "No backup created for jobs.json" + assert len([f for f in iter0_output_files if f.startswith("jobs.json")]) > 1, ( + "No backup created for jobs.json" + ) @pytest.mark.usefixtures("use_tmpdir") 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 435b536812f..ce55463f374 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 @@ -471,8 +471,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, ]