ert 19.0.1__py3-none-any.whl → 20.0.0b1__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.
- ert/__main__.py +94 -63
- ert/analysis/_es_update.py +11 -14
- ert/cli/main.py +1 -1
- ert/config/__init__.py +3 -2
- ert/config/_create_observation_dataframes.py +52 -375
- ert/config/_observations.py +527 -200
- ert/config/_read_summary.py +4 -5
- ert/config/ert_config.py +52 -117
- ert/config/everest_control.py +40 -39
- ert/config/everest_response.py +3 -15
- ert/config/field.py +4 -76
- ert/config/forward_model_step.py +17 -1
- ert/config/gen_data_config.py +14 -17
- ert/config/observation_config_migrations.py +821 -0
- ert/config/parameter_config.py +18 -28
- ert/config/parsing/__init__.py +0 -1
- ert/config/parsing/_parse_zonemap.py +45 -0
- ert/config/parsing/config_keywords.py +1 -0
- ert/config/parsing/config_schema.py +2 -0
- ert/config/parsing/observations_parser.py +2 -0
- ert/config/response_config.py +5 -23
- ert/config/rft_config.py +129 -31
- ert/config/summary_config.py +1 -13
- ert/config/surface_config.py +0 -57
- ert/dark_storage/compute/misfits.py +0 -42
- ert/dark_storage/endpoints/__init__.py +0 -2
- ert/dark_storage/endpoints/experiments.py +2 -5
- ert/dark_storage/json_schema/experiment.py +1 -2
- ert/field_utils/__init__.py +0 -2
- ert/field_utils/field_utils.py +1 -117
- ert/gui/ertwidgets/listeditbox.py +9 -1
- ert/gui/ertwidgets/models/ertsummary.py +20 -6
- ert/gui/ertwidgets/pathchooser.py +9 -1
- ert/gui/ertwidgets/stringbox.py +11 -3
- ert/gui/ertwidgets/textbox.py +10 -3
- ert/gui/ertwidgets/validationsupport.py +19 -1
- ert/gui/main_window.py +11 -6
- ert/gui/simulation/experiment_panel.py +1 -1
- ert/gui/simulation/run_dialog.py +11 -1
- ert/gui/tools/manage_experiments/export_dialog.py +4 -0
- ert/gui/tools/manage_experiments/manage_experiments_panel.py +1 -0
- ert/gui/tools/manage_experiments/storage_info_widget.py +1 -1
- ert/gui/tools/manage_experiments/storage_widget.py +21 -4
- ert/gui/tools/plot/data_type_proxy_model.py +1 -1
- ert/gui/tools/plot/plot_api.py +35 -27
- ert/gui/tools/plot/plot_widget.py +5 -0
- ert/gui/tools/plot/plot_window.py +4 -7
- ert/run_models/ensemble_experiment.py +2 -9
- ert/run_models/ensemble_smoother.py +1 -9
- ert/run_models/everest_run_model.py +31 -23
- ert/run_models/initial_ensemble_run_model.py +19 -22
- ert/run_models/manual_update.py +11 -5
- ert/run_models/model_factory.py +7 -7
- ert/run_models/multiple_data_assimilation.py +3 -16
- ert/sample_prior.py +12 -14
- ert/scheduler/job.py +24 -4
- ert/services/__init__.py +7 -3
- ert/services/_storage_main.py +59 -22
- ert/services/ert_server.py +186 -24
- ert/shared/version.py +3 -3
- ert/storage/local_ensemble.py +50 -116
- ert/storage/local_experiment.py +94 -109
- ert/storage/local_storage.py +10 -12
- ert/storage/migration/to24.py +26 -0
- ert/storage/migration/to25.py +91 -0
- ert/utils/__init__.py +20 -0
- {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/METADATA +4 -51
- {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/RECORD +80 -83
- everest/bin/everest_script.py +5 -5
- everest/bin/kill_script.py +2 -2
- everest/bin/monitor_script.py +2 -2
- everest/bin/utils.py +4 -4
- everest/detached/everserver.py +6 -6
- everest/gui/everest_client.py +0 -6
- everest/gui/main_window.py +2 -2
- everest/util/__init__.py +1 -19
- ert/dark_storage/compute/__init__.py +0 -0
- ert/dark_storage/endpoints/compute/__init__.py +0 -0
- ert/dark_storage/endpoints/compute/misfits.py +0 -95
- ert/services/_base_service.py +0 -387
- ert/services/webviz_ert_service.py +0 -20
- ert/shared/storage/command.py +0 -38
- ert/shared/storage/extraction.py +0 -42
- {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/WHEEL +0 -0
- {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/entry_points.txt +0 -0
- {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/licenses/COPYING +0 -0
- {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/top_level.txt +0 -0
ert/scheduler/job.py
CHANGED
|
@@ -482,6 +482,7 @@ async def log_warnings_from_forward_model(
|
|
|
482
482
|
real: Realization,
|
|
483
483
|
job_submission_time: float,
|
|
484
484
|
timeout_seconds: int = Job.DEFAULT_FILE_VERIFICATION_TIMEOUT,
|
|
485
|
+
max_logged_warnings: int = 200,
|
|
485
486
|
) -> int:
|
|
486
487
|
"""Parse all stdout and stderr files from running the forward model
|
|
487
488
|
for anything that looks like a Warning, and log it.
|
|
@@ -513,11 +514,19 @@ async def log_warnings_from_forward_model(
|
|
|
513
514
|
)
|
|
514
515
|
|
|
515
516
|
async def log_warnings_from_file(
|
|
516
|
-
file: Path,
|
|
517
|
-
|
|
517
|
+
file: Path,
|
|
518
|
+
iens: int,
|
|
519
|
+
step: ForwardModelStep,
|
|
520
|
+
step_idx: int,
|
|
521
|
+
filetype: str,
|
|
522
|
+
max_warnings_to_log: int,
|
|
523
|
+
) -> int:
|
|
524
|
+
"""Returns how many times a warning was logged"""
|
|
518
525
|
captured: list[str] = []
|
|
519
526
|
file_text = await anyio.Path(file).read_text(encoding="utf-8")
|
|
520
527
|
for line in file_text.splitlines():
|
|
528
|
+
if len(captured) >= max_warnings_to_log:
|
|
529
|
+
break
|
|
521
530
|
if line_contains_warning(line):
|
|
522
531
|
captured.append(line[:max_length])
|
|
523
532
|
|
|
@@ -528,6 +537,7 @@ async def log_warnings_from_forward_model(
|
|
|
528
537
|
)
|
|
529
538
|
warnings.warn(warning_msg, PostSimulationWarning, stacklevel=2)
|
|
530
539
|
logger.warning(warning_msg)
|
|
540
|
+
return len(captured)
|
|
531
541
|
|
|
532
542
|
async def wait_for_file(file_path: Path, _timeout: int) -> int:
|
|
533
543
|
if _timeout <= 0:
|
|
@@ -546,6 +556,7 @@ async def log_warnings_from_forward_model(
|
|
|
546
556
|
break
|
|
547
557
|
return remaining_timeout
|
|
548
558
|
|
|
559
|
+
log_count = 0
|
|
549
560
|
with suppress(KeyError):
|
|
550
561
|
runpath = Path(real.run_arg.runpath)
|
|
551
562
|
for step_idx, step in enumerate(real.fm_steps):
|
|
@@ -560,9 +571,18 @@ async def log_warnings_from_forward_model(
|
|
|
560
571
|
if timeout_seconds <= 0:
|
|
561
572
|
break
|
|
562
573
|
|
|
563
|
-
await log_warnings_from_file(
|
|
564
|
-
std_path,
|
|
574
|
+
log_count += await log_warnings_from_file(
|
|
575
|
+
std_path,
|
|
576
|
+
real.iens,
|
|
577
|
+
step,
|
|
578
|
+
step_idx,
|
|
579
|
+
file_type,
|
|
580
|
+
max_logged_warnings - log_count,
|
|
565
581
|
)
|
|
566
582
|
if timeout_seconds <= 0:
|
|
567
583
|
break
|
|
584
|
+
if log_count >= max_logged_warnings:
|
|
585
|
+
logger.warning(
|
|
586
|
+
"Reached maximum number of forward model step warnings to extract"
|
|
587
|
+
)
|
|
568
588
|
return timeout_seconds
|
ert/services/__init__.py
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
from .ert_server import
|
|
2
|
-
|
|
1
|
+
from .ert_server import (
|
|
2
|
+
ErtServer,
|
|
3
|
+
ErtServerExit,
|
|
4
|
+
ServerBootFail,
|
|
5
|
+
create_ertserver_client,
|
|
6
|
+
)
|
|
3
7
|
|
|
4
|
-
__all__ = ["ErtServer", "
|
|
8
|
+
__all__ = ["ErtServer", "ErtServerExit", "ServerBootFail", "create_ertserver_client"]
|
ert/services/_storage_main.py
CHANGED
|
@@ -13,6 +13,7 @@ import sys
|
|
|
13
13
|
import threading
|
|
14
14
|
import time
|
|
15
15
|
import warnings
|
|
16
|
+
from argparse import ArgumentParser
|
|
16
17
|
from base64 import b64encode
|
|
17
18
|
from pathlib import Path
|
|
18
19
|
from typing import Any
|
|
@@ -29,12 +30,11 @@ from uvicorn.supervisors import ChangeReload
|
|
|
29
30
|
|
|
30
31
|
from ert.logging import STORAGE_LOG_CONFIG
|
|
31
32
|
from ert.plugins import setup_site_logging
|
|
32
|
-
from ert.services
|
|
33
|
+
from ert.services import ErtServerExit
|
|
33
34
|
from ert.shared import __file__ as ert_shared_path
|
|
34
35
|
from ert.shared import find_available_socket, get_machine_name
|
|
35
|
-
from ert.shared.storage.command import add_parser_options
|
|
36
36
|
from ert.trace import tracer
|
|
37
|
-
from
|
|
37
|
+
from ert.utils import makedirs_if_needed
|
|
38
38
|
|
|
39
39
|
DARK_STORAGE_APP = "ert.dark_storage.app:app"
|
|
40
40
|
|
|
@@ -82,7 +82,7 @@ def _get_host_list() -> list[str]:
|
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
def _create_connection_info(
|
|
85
|
-
sock: socket.socket, authtoken: str, cert: str | os.PathLike[str]
|
|
85
|
+
sock: socket.socket, authtoken: str, cert: str | os.PathLike[str] | Path
|
|
86
86
|
) -> dict[str, Any]:
|
|
87
87
|
connection_info = {
|
|
88
88
|
"urls": [
|
|
@@ -91,7 +91,7 @@ def _create_connection_info(
|
|
|
91
91
|
"authtoken": authtoken,
|
|
92
92
|
"host": get_machine_name(),
|
|
93
93
|
"port": sock.getsockname()[1],
|
|
94
|
-
"cert": cert,
|
|
94
|
+
"cert": str(cert),
|
|
95
95
|
"auth": authtoken,
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -102,14 +102,17 @@ def _create_connection_info(
|
|
|
102
102
|
return connection_info
|
|
103
103
|
|
|
104
104
|
|
|
105
|
-
def _generate_certificate(cert_folder:
|
|
105
|
+
def _generate_certificate(cert_folder: Path) -> tuple[Path, Path, bytes]:
|
|
106
106
|
"""Generate a private key and a certificate signed with it
|
|
107
107
|
|
|
108
108
|
Both the certificate and the key are written to files in the folder given
|
|
109
109
|
by `get_certificate_dir(config)`. The key is encrypted before being
|
|
110
110
|
stored.
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
|
|
112
|
+
Returns a 3-tuple with
|
|
113
|
+
* Certificate file path
|
|
114
|
+
* Key file path
|
|
115
|
+
* Password used for encrypting the key
|
|
113
116
|
"""
|
|
114
117
|
# Generate private key
|
|
115
118
|
key = rsa.generate_private_key(
|
|
@@ -150,11 +153,11 @@ def _generate_certificate(cert_folder: str) -> tuple[str, str, bytes]:
|
|
|
150
153
|
|
|
151
154
|
# Write certificate and key to disk
|
|
152
155
|
makedirs_if_needed(cert_folder)
|
|
153
|
-
cert_path =
|
|
154
|
-
|
|
155
|
-
key_path =
|
|
156
|
+
cert_path = cert_folder / f"{dns_name}.crt"
|
|
157
|
+
cert_path.write_bytes(cert.public_bytes(serialization.Encoding.PEM))
|
|
158
|
+
key_path = cert_folder / f"{dns_name}.key"
|
|
156
159
|
pw = bytes(os.urandom(28))
|
|
157
|
-
|
|
160
|
+
key_path.write_bytes(
|
|
158
161
|
key.private_bytes(
|
|
159
162
|
encoding=serialization.Encoding.PEM,
|
|
160
163
|
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
|
@@ -184,16 +187,16 @@ def run_server(
|
|
|
184
187
|
|
|
185
188
|
config_args: dict[str, Any] = {}
|
|
186
189
|
if args.debug or debug:
|
|
187
|
-
config_args.update(reload=True, reload_dirs=[
|
|
190
|
+
config_args.update(reload=True, reload_dirs=[Path(ert_shared_path).parent])
|
|
188
191
|
os.environ["ERT_STORAGE_DEBUG"] = "1"
|
|
189
192
|
|
|
190
|
-
sock = find_available_socket(
|
|
193
|
+
sock: socket.socket = find_available_socket(
|
|
191
194
|
host=get_machine_name(), port_range=range(51850, 51870 + 1)
|
|
192
195
|
)
|
|
193
196
|
|
|
194
197
|
# Appropriated from uvicorn.main:run
|
|
195
198
|
os.environ["ERT_STORAGE_NO_TOKEN"] = "1"
|
|
196
|
-
os.environ["ERT_STORAGE_ENS_PATH"] =
|
|
199
|
+
os.environ["ERT_STORAGE_ENS_PATH"] = str(args.project.absolute())
|
|
197
200
|
config = (
|
|
198
201
|
# uvicorn.Config() resets the logging config (overriding additional
|
|
199
202
|
# handlers added to loggers like e.g. the ert_azurelogger handler
|
|
@@ -253,11 +256,11 @@ def _join_terminate_thread(terminate_on_parent_death_thread: threading.Thread) -
|
|
|
253
256
|
"""Join the terminate thread, handling BaseServiceExit (which is used by Everest)"""
|
|
254
257
|
try:
|
|
255
258
|
terminate_on_parent_death_thread.join()
|
|
256
|
-
except
|
|
259
|
+
except ErtServerExit:
|
|
257
260
|
logger = logging.getLogger("ert.shared.storage.info")
|
|
258
261
|
logger.info(
|
|
259
262
|
"Got BaseServiceExit while joining terminate thread, "
|
|
260
|
-
"as expected from
|
|
263
|
+
"as expected from ert_server.py"
|
|
261
264
|
)
|
|
262
265
|
|
|
263
266
|
|
|
@@ -265,9 +268,7 @@ def main() -> None:
|
|
|
265
268
|
args = parse_args()
|
|
266
269
|
authentication = _generate_authentication()
|
|
267
270
|
os.environ["ERT_STORAGE_TOKEN"] = authentication
|
|
268
|
-
cert_path, key_path, key_pw = _generate_certificate(
|
|
269
|
-
os.path.join(args.project, "cert")
|
|
270
|
-
)
|
|
271
|
+
cert_path, key_path, key_pw = _generate_certificate(args.project / "cert")
|
|
271
272
|
config_args: dict[str, Any] = {
|
|
272
273
|
"ssl_keyfile": key_path,
|
|
273
274
|
"ssl_certfile": cert_path,
|
|
@@ -283,7 +284,7 @@ def main() -> None:
|
|
|
283
284
|
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
|
284
285
|
|
|
285
286
|
if args.debug:
|
|
286
|
-
config_args.update(reload=True, reload_dirs=[
|
|
287
|
+
config_args.update(reload=True, reload_dirs=[Path(ert_shared_path).parent])
|
|
287
288
|
|
|
288
289
|
# Need to run uvicorn.Config before entering the ErtPluginContext because
|
|
289
290
|
# uvicorn.Config overrides the configuration of existing loggers, thus removing
|
|
@@ -310,12 +311,48 @@ def main() -> None:
|
|
|
310
311
|
logger.info("Starting dark storage")
|
|
311
312
|
logger.info(f"Started dark storage with parent {args.parent_pid}")
|
|
312
313
|
run_server(args, debug=False, uvicorn_config=uvicorn_config)
|
|
313
|
-
except (SystemExit,
|
|
314
|
+
except (SystemExit, ErtServerExit):
|
|
314
315
|
logger.info("Stopping dark storage")
|
|
315
316
|
finally:
|
|
316
317
|
stopped.set()
|
|
317
318
|
_join_terminate_thread(terminate_on_parent_death_thread)
|
|
318
319
|
|
|
319
320
|
|
|
321
|
+
def add_parser_options(ap: ArgumentParser) -> None:
|
|
322
|
+
ap.add_argument(
|
|
323
|
+
"config",
|
|
324
|
+
type=str,
|
|
325
|
+
help=("ERT config file to start the server from "),
|
|
326
|
+
nargs="?", # optional
|
|
327
|
+
)
|
|
328
|
+
ap.add_argument(
|
|
329
|
+
"--project",
|
|
330
|
+
"-p",
|
|
331
|
+
type=Path,
|
|
332
|
+
help="Path to directory in which to create storage_server.json",
|
|
333
|
+
default=Path.cwd(),
|
|
334
|
+
)
|
|
335
|
+
ap.add_argument(
|
|
336
|
+
"--traceparent",
|
|
337
|
+
type=str,
|
|
338
|
+
help="Trace parent id to be used by the storage root span",
|
|
339
|
+
default=None,
|
|
340
|
+
)
|
|
341
|
+
ap.add_argument(
|
|
342
|
+
"--parent_pid",
|
|
343
|
+
type=int,
|
|
344
|
+
help="The parent process id",
|
|
345
|
+
default=os.getppid(),
|
|
346
|
+
)
|
|
347
|
+
ap.add_argument(
|
|
348
|
+
"--host", type=str, default=os.environ.get("ERT_STORAGE_HOST", "127.0.0.1")
|
|
349
|
+
)
|
|
350
|
+
ap.add_argument("--logging-config", type=str, default=None)
|
|
351
|
+
ap.add_argument(
|
|
352
|
+
"--verbose", action="store_true", help="Show verbose output.", default=False
|
|
353
|
+
)
|
|
354
|
+
ap.add_argument("--debug", action="store_true", default=False)
|
|
355
|
+
|
|
356
|
+
|
|
320
357
|
if __name__ == "__main__":
|
|
321
358
|
main()
|
ert/services/ert_server.py
CHANGED
|
@@ -1,23 +1,187 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
4
|
+
import io
|
|
3
5
|
import json
|
|
4
6
|
import logging
|
|
5
7
|
import os
|
|
8
|
+
import signal
|
|
6
9
|
import sys
|
|
7
10
|
import threading
|
|
8
11
|
import types
|
|
9
|
-
from collections.abc import Mapping
|
|
12
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
10
13
|
from pathlib import Path
|
|
14
|
+
from select import PIPE_BUF, select
|
|
15
|
+
from subprocess import Popen, TimeoutExpired
|
|
11
16
|
from tempfile import NamedTemporaryFile
|
|
12
17
|
from time import sleep
|
|
13
|
-
from typing import Any, cast
|
|
18
|
+
from typing import Any, TypedDict, cast
|
|
14
19
|
|
|
15
20
|
import requests
|
|
16
21
|
|
|
17
22
|
from ert.dark_storage.client import Client, ErtClientConnectionInfo
|
|
18
|
-
from ert.services._base_service import ErtServerConnectionInfo, _Proc
|
|
19
23
|
from ert.trace import get_traceparent
|
|
20
24
|
|
|
25
|
+
SERVICE_CONF_PATHS: set[str] = set()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ErtServerConnectionInfo(TypedDict):
|
|
29
|
+
urls: list[str]
|
|
30
|
+
authtoken: str
|
|
31
|
+
host: str
|
|
32
|
+
port: str
|
|
33
|
+
cert: str
|
|
34
|
+
auth: str
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ErtServerExit(OSError):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def cleanup_service_files(signum: int, frame: types.FrameType | None) -> None:
|
|
42
|
+
for file_path in SERVICE_CONF_PATHS:
|
|
43
|
+
file = Path(file_path)
|
|
44
|
+
if file.exists():
|
|
45
|
+
file.unlink()
|
|
46
|
+
raise ErtServerExit(f"Signal {signum} received.")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
if threading.current_thread() is threading.main_thread():
|
|
50
|
+
signal.signal(signal.SIGTERM, cleanup_service_files)
|
|
51
|
+
signal.signal(signal.SIGINT, cleanup_service_files)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ServerBootFail(RuntimeError):
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class _Proc(threading.Thread):
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
service_name: str,
|
|
62
|
+
exec_args: Sequence[str],
|
|
63
|
+
timeout: int,
|
|
64
|
+
on_connection_info_received: Callable[
|
|
65
|
+
[ErtServerConnectionInfo | Exception | None], None
|
|
66
|
+
],
|
|
67
|
+
project: Path,
|
|
68
|
+
) -> None:
|
|
69
|
+
super().__init__()
|
|
70
|
+
|
|
71
|
+
self._shutdown = threading.Event()
|
|
72
|
+
|
|
73
|
+
self._service_name = service_name
|
|
74
|
+
self._exec_args = exec_args
|
|
75
|
+
self._timeout = timeout
|
|
76
|
+
self._propagate_connection_info_from_childproc = on_connection_info_received
|
|
77
|
+
self._service_config_path = project / f"{self._service_name}_server.json"
|
|
78
|
+
|
|
79
|
+
fd_read, fd_write = os.pipe()
|
|
80
|
+
self._comm_pipe = os.fdopen(fd_read)
|
|
81
|
+
|
|
82
|
+
env = os.environ.copy()
|
|
83
|
+
env["ERT_COMM_FD"] = str(fd_write)
|
|
84
|
+
|
|
85
|
+
SERVICE_CONF_PATHS.add(str(self._service_config_path))
|
|
86
|
+
|
|
87
|
+
# The process is waited for in _do_shutdown()
|
|
88
|
+
self._childproc = Popen(
|
|
89
|
+
self._exec_args,
|
|
90
|
+
pass_fds=(fd_write,),
|
|
91
|
+
env=env,
|
|
92
|
+
close_fds=True,
|
|
93
|
+
)
|
|
94
|
+
os.close(fd_write)
|
|
95
|
+
|
|
96
|
+
def run(self) -> None:
|
|
97
|
+
comm = self._read_connection_info_from_process(self._childproc)
|
|
98
|
+
|
|
99
|
+
if comm is None:
|
|
100
|
+
self._propagate_connection_info_from_childproc(TimeoutError())
|
|
101
|
+
return # _read_conn_info() has already cleaned up in this case
|
|
102
|
+
|
|
103
|
+
conn_info: ErtServerConnectionInfo | Exception | None = None
|
|
104
|
+
try:
|
|
105
|
+
conn_info = json.loads(comm)
|
|
106
|
+
except json.JSONDecodeError:
|
|
107
|
+
conn_info = ServerBootFail()
|
|
108
|
+
except Exception as exc:
|
|
109
|
+
conn_info = exc
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
self._propagate_connection_info_from_childproc(conn_info)
|
|
113
|
+
|
|
114
|
+
while True:
|
|
115
|
+
if self._childproc.poll() is not None:
|
|
116
|
+
break
|
|
117
|
+
if self._shutdown.wait(1):
|
|
118
|
+
self._do_shutdown()
|
|
119
|
+
break
|
|
120
|
+
|
|
121
|
+
except Exception as e:
|
|
122
|
+
print(str(e))
|
|
123
|
+
self.logger.exception(e)
|
|
124
|
+
|
|
125
|
+
finally:
|
|
126
|
+
self._ensure_connection_info_file_is_deleted()
|
|
127
|
+
|
|
128
|
+
def shutdown(self) -> int:
|
|
129
|
+
"""Shutdown the server."""
|
|
130
|
+
self._shutdown.set()
|
|
131
|
+
self.join()
|
|
132
|
+
|
|
133
|
+
return self._childproc.returncode
|
|
134
|
+
|
|
135
|
+
def _read_connection_info_from_process(self, proc: Popen[bytes]) -> str | None:
|
|
136
|
+
comm_buf = io.StringIO()
|
|
137
|
+
first_iter = True
|
|
138
|
+
while first_iter or proc.poll() is None:
|
|
139
|
+
first_iter = False
|
|
140
|
+
ready = select([self._comm_pipe], [], [], self._timeout)
|
|
141
|
+
|
|
142
|
+
# Timeout reached, exit with a failure
|
|
143
|
+
if ready == ([], [], []):
|
|
144
|
+
self._do_shutdown()
|
|
145
|
+
self._ensure_connection_info_file_is_deleted()
|
|
146
|
+
return None
|
|
147
|
+
|
|
148
|
+
x = self._comm_pipe.read(PIPE_BUF)
|
|
149
|
+
if not x: # EOF
|
|
150
|
+
break
|
|
151
|
+
comm_buf.write(x)
|
|
152
|
+
return comm_buf.getvalue()
|
|
153
|
+
|
|
154
|
+
def _do_shutdown(self) -> None:
|
|
155
|
+
if self._childproc is None:
|
|
156
|
+
return
|
|
157
|
+
try:
|
|
158
|
+
self._childproc.terminate()
|
|
159
|
+
self._childproc.wait(10) # Give it 10s to shut down cleanly..
|
|
160
|
+
except TimeoutExpired:
|
|
161
|
+
try:
|
|
162
|
+
self._childproc.kill() # ... then kick it harder...
|
|
163
|
+
self._childproc.wait(self._timeout) # ... and wait again
|
|
164
|
+
except TimeoutExpired:
|
|
165
|
+
self.logger.error(
|
|
166
|
+
f"waiting for child-process exceeded timeout {self._timeout}s"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
def _ensure_connection_info_file_is_deleted(self) -> None:
|
|
170
|
+
"""
|
|
171
|
+
Ensure that the JSON connection information file is deleted
|
|
172
|
+
"""
|
|
173
|
+
with contextlib.suppress(OSError):
|
|
174
|
+
if self._service_config_path.exists():
|
|
175
|
+
self._service_config_path.unlink()
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def logger(self) -> logging.Logger:
|
|
179
|
+
return logging.getLogger("ert.shared.storage")
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
_ERT_SERVER_CONNECTION_INFO_FILE = "storage_server.json"
|
|
183
|
+
_ERT_SERVER_EXECUTABLE_FILE = str(Path(__file__).parent / "_storage_main.py")
|
|
184
|
+
|
|
21
185
|
|
|
22
186
|
class ErtServerContext:
|
|
23
187
|
def __init__(self, service: ErtServer) -> None:
|
|
@@ -70,7 +234,7 @@ class ErtServer:
|
|
|
70
234
|
|
|
71
235
|
run_storage_main_cmd = [
|
|
72
236
|
sys.executable,
|
|
73
|
-
|
|
237
|
+
_ERT_SERVER_EXECUTABLE_FILE,
|
|
74
238
|
"--project",
|
|
75
239
|
storage_path,
|
|
76
240
|
]
|
|
@@ -91,7 +255,7 @@ class ErtServer:
|
|
|
91
255
|
self._thread_that_starts_server_process = _Proc(
|
|
92
256
|
service_name="storage",
|
|
93
257
|
exec_args=run_storage_main_cmd,
|
|
94
|
-
timeout=
|
|
258
|
+
timeout=timeout,
|
|
95
259
|
on_connection_info_received=self.on_connection_info_received_from_server_process,
|
|
96
260
|
project=Path(self._storage_path),
|
|
97
261
|
)
|
|
@@ -167,21 +331,6 @@ class ErtServer:
|
|
|
167
331
|
"None of the URLs provided for the ert storage server worked."
|
|
168
332
|
)
|
|
169
333
|
|
|
170
|
-
@classmethod
|
|
171
|
-
def session(cls, project: os.PathLike[str], timeout: int | None = None) -> Client:
|
|
172
|
-
"""
|
|
173
|
-
Start a HTTP transaction with the server
|
|
174
|
-
"""
|
|
175
|
-
inst = cls.connect(timeout=timeout, project=project)
|
|
176
|
-
info = inst.fetch_connection_info()
|
|
177
|
-
return Client(
|
|
178
|
-
conn_info=ErtClientConnectionInfo(
|
|
179
|
-
base_url=inst.fetch_url(),
|
|
180
|
-
auth_token=inst.fetch_auth()[1],
|
|
181
|
-
cert=info["cert"],
|
|
182
|
-
)
|
|
183
|
-
)
|
|
184
|
-
|
|
185
334
|
@property
|
|
186
335
|
def logger(self) -> logging.Logger:
|
|
187
336
|
return logging.getLogger("ert.shared.storage")
|
|
@@ -218,12 +367,12 @@ class ErtServer:
|
|
|
218
367
|
timeout = 240
|
|
219
368
|
t = -1
|
|
220
369
|
while t < timeout:
|
|
221
|
-
storage_server_path = path /
|
|
370
|
+
storage_server_path = path / _ERT_SERVER_CONNECTION_INFO_FILE
|
|
222
371
|
if (
|
|
223
372
|
storage_server_path.exists()
|
|
224
373
|
and storage_server_path.stat().st_size > 0
|
|
225
374
|
):
|
|
226
|
-
with (path /
|
|
375
|
+
with (path / _ERT_SERVER_CONNECTION_INFO_FILE).open() as f:
|
|
227
376
|
storage_server_content = json.load(f)
|
|
228
377
|
|
|
229
378
|
return ErtServer(
|
|
@@ -277,9 +426,9 @@ class ErtServer:
|
|
|
277
426
|
if self._storage_path is not None:
|
|
278
427
|
if not Path(self._storage_path).exists():
|
|
279
428
|
raise RuntimeError(f"No storage exists at : {self._storage_path}")
|
|
280
|
-
path = f"{self._storage_path}/
|
|
429
|
+
path = f"{self._storage_path}/{_ERT_SERVER_CONNECTION_INFO_FILE}"
|
|
281
430
|
else:
|
|
282
|
-
path =
|
|
431
|
+
path = _ERT_SERVER_CONNECTION_INFO_FILE
|
|
283
432
|
|
|
284
433
|
if isinstance(info, Mapping):
|
|
285
434
|
with NamedTemporaryFile(dir=f"{self._storage_path}", delete=False) as f:
|
|
@@ -315,3 +464,16 @@ class ErtServer:
|
|
|
315
464
|
def wait(self) -> None:
|
|
316
465
|
if self._thread_that_starts_server_process is not None:
|
|
317
466
|
self._thread_that_starts_server_process.join()
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
def create_ertserver_client(project: Path, timeout: int | None = None) -> Client:
|
|
470
|
+
"""Read connection info from file in path and create HTTP client."""
|
|
471
|
+
connection = ErtServer.connect(timeout=timeout, project=project)
|
|
472
|
+
info = connection.fetch_connection_info()
|
|
473
|
+
return Client(
|
|
474
|
+
conn_info=ErtClientConnectionInfo(
|
|
475
|
+
base_url=connection.fetch_url(),
|
|
476
|
+
auth_token=connection.fetch_auth()[1],
|
|
477
|
+
cert=info["cert"],
|
|
478
|
+
)
|
|
479
|
+
)
|
ert/shared/version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '
|
|
32
|
-
__version_tuple__ = version_tuple = (
|
|
31
|
+
__version__ = version = '20.0.0b1'
|
|
32
|
+
__version_tuple__ = version_tuple = (20, 0, 0, 'b1')
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'g4156859a8'
|