vortex-nwp 2.0.0b1__py3-none-any.whl → 2.1.0__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.
- vortex/__init__.py +75 -47
- vortex/algo/__init__.py +3 -2
- vortex/algo/components.py +944 -618
- vortex/algo/mpitools.py +802 -497
- vortex/algo/mpitools_templates/__init__.py +1 -0
- vortex/algo/serversynctools.py +34 -33
- vortex/config.py +19 -22
- vortex/data/__init__.py +9 -3
- vortex/data/abstractstores.py +593 -655
- vortex/data/containers.py +217 -162
- vortex/data/contents.py +65 -39
- vortex/data/executables.py +93 -102
- vortex/data/flow.py +40 -34
- vortex/data/geometries.py +228 -132
- vortex/data/handlers.py +436 -227
- vortex/data/outflow.py +15 -15
- vortex/data/providers.py +185 -163
- vortex/data/resources.py +48 -42
- vortex/data/stores.py +540 -417
- vortex/data/sync_templates/__init__.py +0 -0
- vortex/gloves.py +114 -87
- vortex/layout/__init__.py +1 -8
- vortex/layout/contexts.py +150 -84
- vortex/layout/dataflow.py +353 -202
- vortex/layout/monitor.py +264 -128
- vortex/nwp/__init__.py +5 -2
- vortex/nwp/algo/__init__.py +14 -5
- vortex/nwp/algo/assim.py +205 -151
- vortex/nwp/algo/clim.py +683 -517
- vortex/nwp/algo/coupling.py +447 -225
- vortex/nwp/algo/eda.py +437 -229
- vortex/nwp/algo/eps.py +403 -231
- vortex/nwp/algo/forecasts.py +416 -275
- vortex/nwp/algo/fpserver.py +683 -307
- vortex/nwp/algo/ifsnaming.py +205 -145
- vortex/nwp/algo/ifsroot.py +215 -122
- vortex/nwp/algo/monitoring.py +137 -76
- vortex/nwp/algo/mpitools.py +330 -190
- vortex/nwp/algo/odbtools.py +637 -353
- vortex/nwp/algo/oopsroot.py +454 -273
- vortex/nwp/algo/oopstests.py +90 -56
- vortex/nwp/algo/request.py +287 -206
- vortex/nwp/algo/stdpost.py +878 -522
- vortex/nwp/data/__init__.py +22 -4
- vortex/nwp/data/assim.py +125 -137
- vortex/nwp/data/boundaries.py +121 -68
- vortex/nwp/data/climfiles.py +193 -211
- vortex/nwp/data/configfiles.py +73 -69
- vortex/nwp/data/consts.py +426 -401
- vortex/nwp/data/ctpini.py +59 -43
- vortex/nwp/data/diagnostics.py +94 -66
- vortex/nwp/data/eda.py +50 -51
- vortex/nwp/data/eps.py +195 -146
- vortex/nwp/data/executables.py +440 -434
- vortex/nwp/data/fields.py +63 -48
- vortex/nwp/data/gridfiles.py +183 -111
- vortex/nwp/data/logs.py +250 -217
- vortex/nwp/data/modelstates.py +180 -151
- vortex/nwp/data/monitoring.py +72 -99
- vortex/nwp/data/namelists.py +254 -202
- vortex/nwp/data/obs.py +400 -308
- vortex/nwp/data/oopsexec.py +22 -20
- vortex/nwp/data/providers.py +90 -65
- vortex/nwp/data/query.py +71 -82
- vortex/nwp/data/stores.py +49 -36
- vortex/nwp/data/surfex.py +136 -137
- vortex/nwp/syntax/__init__.py +1 -1
- vortex/nwp/syntax/stdattrs.py +173 -111
- vortex/nwp/tools/__init__.py +2 -2
- vortex/nwp/tools/addons.py +22 -17
- vortex/nwp/tools/agt.py +24 -12
- vortex/nwp/tools/bdap.py +16 -5
- vortex/nwp/tools/bdcp.py +4 -1
- vortex/nwp/tools/bdm.py +3 -0
- vortex/nwp/tools/bdmp.py +14 -9
- vortex/nwp/tools/conftools.py +728 -378
- vortex/nwp/tools/drhook.py +12 -8
- vortex/nwp/tools/grib.py +65 -39
- vortex/nwp/tools/gribdiff.py +22 -17
- vortex/nwp/tools/ifstools.py +82 -42
- vortex/nwp/tools/igastuff.py +167 -143
- vortex/nwp/tools/mars.py +14 -2
- vortex/nwp/tools/odb.py +234 -125
- vortex/nwp/tools/partitioning.py +61 -37
- vortex/nwp/tools/satrad.py +27 -12
- vortex/nwp/util/async.py +83 -55
- vortex/nwp/util/beacon.py +10 -10
- vortex/nwp/util/diffpygram.py +174 -86
- vortex/nwp/util/ens.py +144 -63
- vortex/nwp/util/hooks.py +30 -19
- vortex/nwp/util/taskdeco.py +28 -24
- vortex/nwp/util/usepygram.py +278 -172
- vortex/nwp/util/usetnt.py +31 -17
- vortex/sessions.py +72 -39
- vortex/syntax/__init__.py +1 -1
- vortex/syntax/stdattrs.py +410 -171
- vortex/syntax/stddeco.py +31 -22
- vortex/toolbox.py +327 -192
- vortex/tools/__init__.py +11 -2
- vortex/tools/actions.py +110 -121
- vortex/tools/addons.py +111 -92
- vortex/tools/arm.py +42 -22
- vortex/tools/compression.py +72 -69
- vortex/tools/date.py +11 -4
- vortex/tools/delayedactions.py +242 -132
- vortex/tools/env.py +75 -47
- vortex/tools/folder.py +342 -171
- vortex/tools/grib.py +341 -162
- vortex/tools/lfi.py +423 -216
- vortex/tools/listings.py +109 -40
- vortex/tools/names.py +218 -156
- vortex/tools/net.py +655 -299
- vortex/tools/parallelism.py +93 -61
- vortex/tools/prestaging.py +55 -31
- vortex/tools/schedulers.py +172 -105
- vortex/tools/services.py +403 -334
- vortex/tools/storage.py +293 -358
- vortex/tools/surfex.py +24 -24
- vortex/tools/systems.py +1234 -643
- vortex/tools/targets.py +156 -100
- vortex/util/__init__.py +1 -1
- vortex/util/config.py +378 -327
- vortex/util/empty.py +2 -2
- vortex/util/helpers.py +56 -24
- vortex/util/introspection.py +18 -12
- vortex/util/iosponge.py +8 -4
- vortex/util/roles.py +4 -6
- vortex/util/storefunctions.py +39 -13
- vortex/util/structs.py +3 -3
- vortex/util/worker.py +29 -17
- vortex_nwp-2.1.0.dist-info/METADATA +67 -0
- vortex_nwp-2.1.0.dist-info/RECORD +144 -0
- {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.1.0.dist-info}/WHEEL +1 -1
- vortex/layout/appconf.py +0 -109
- vortex/layout/jobs.py +0 -1276
- vortex/layout/nodes.py +0 -1424
- vortex/layout/subjobs.py +0 -464
- vortex_nwp-2.0.0b1.dist-info/METADATA +0 -50
- vortex_nwp-2.0.0b1.dist-info/RECORD +0 -146
- {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.1.0.dist-info/licenses}/LICENSE +0 -0
- {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.1.0.dist-info}/top_level.txt +0 -0
vortex/tools/schedulers.py
CHANGED
|
@@ -8,6 +8,7 @@ import functools
|
|
|
8
8
|
from bronx.fancies import loggers
|
|
9
9
|
import footprints
|
|
10
10
|
|
|
11
|
+
from vortex import config
|
|
11
12
|
from .services import Service
|
|
12
13
|
|
|
13
14
|
__all__ = []
|
|
@@ -20,18 +21,18 @@ class Scheduler(Service):
|
|
|
20
21
|
|
|
21
22
|
_abstract = True
|
|
22
23
|
_footprint = dict(
|
|
23
|
-
info
|
|
24
|
-
attr
|
|
25
|
-
muteset
|
|
26
|
-
optional
|
|
27
|
-
default
|
|
28
|
-
type
|
|
24
|
+
info="Scheduling service class",
|
|
25
|
+
attr=dict(
|
|
26
|
+
muteset=dict(
|
|
27
|
+
optional=True,
|
|
28
|
+
default=footprints.FPSet(),
|
|
29
|
+
type=footprints.FPSet,
|
|
29
30
|
)
|
|
30
|
-
)
|
|
31
|
+
),
|
|
31
32
|
)
|
|
32
33
|
|
|
33
34
|
def __init__(self, *args, **kw):
|
|
34
|
-
logger.debug(
|
|
35
|
+
logger.debug("Scheduler init %s", self.__class__)
|
|
35
36
|
super().__init__(*args, **kw)
|
|
36
37
|
|
|
37
38
|
@property
|
|
@@ -61,15 +62,15 @@ class EcmwfLikeScheduler(Scheduler):
|
|
|
61
62
|
|
|
62
63
|
_abstract = True
|
|
63
64
|
_footprint = dict(
|
|
64
|
-
attr
|
|
65
|
-
env_pattern
|
|
66
|
-
info
|
|
65
|
+
attr=dict(
|
|
66
|
+
env_pattern=dict(
|
|
67
|
+
info="Scheduler configuration variables start with...",
|
|
67
68
|
),
|
|
68
|
-
non_critical_timeout
|
|
69
|
-
info
|
|
70
|
-
type
|
|
71
|
-
default
|
|
72
|
-
optional
|
|
69
|
+
non_critical_timeout=dict(
|
|
70
|
+
info="Timeout in seconds for non-critical commands",
|
|
71
|
+
type=int,
|
|
72
|
+
default=5,
|
|
73
|
+
optional=True,
|
|
73
74
|
),
|
|
74
75
|
)
|
|
75
76
|
)
|
|
@@ -77,17 +78,23 @@ class EcmwfLikeScheduler(Scheduler):
|
|
|
77
78
|
_KNOWN_CMD = ()
|
|
78
79
|
|
|
79
80
|
def __init__(self, *args, **kw):
|
|
80
|
-
logger.debug(
|
|
81
|
+
logger.debug("Scheduler init %s", self.__class__)
|
|
81
82
|
super(Scheduler, self).__init__(*args, **kw)
|
|
82
83
|
self._inside_child_session = False
|
|
83
84
|
|
|
84
85
|
def conf(self, kwenv):
|
|
85
86
|
"""Possibly export the provided variables and return a dictionary of positioned variables."""
|
|
86
87
|
if kwenv:
|
|
87
|
-
for schedvar in [
|
|
88
|
+
for schedvar in [
|
|
89
|
+
x.upper()
|
|
90
|
+
for x in kwenv.keys()
|
|
91
|
+
if x.upper().startswith(self.env_pattern)
|
|
92
|
+
]:
|
|
88
93
|
self.env[schedvar] = str(kwenv[schedvar])
|
|
89
94
|
subenv = dict()
|
|
90
|
-
for schedvar in [
|
|
95
|
+
for schedvar in [
|
|
96
|
+
x for x in self.env.keys() if x.startswith(self.env_pattern)
|
|
97
|
+
]:
|
|
91
98
|
subenv[schedvar] = self.env.get(schedvar)
|
|
92
99
|
return subenv
|
|
93
100
|
|
|
@@ -141,27 +148,45 @@ class EcmwfLikeScheduler(Scheduler):
|
|
|
141
148
|
rc = None
|
|
142
149
|
cmd = self.cmd_rename(cmd)
|
|
143
150
|
if cmd in self.muteset:
|
|
144
|
-
logger.warning(
|
|
151
|
+
logger.warning("%s mute command [%s]", self.kind, cmd)
|
|
145
152
|
else:
|
|
146
153
|
with self.child_session() as session_rc:
|
|
147
154
|
if session_rc:
|
|
148
|
-
if getattr(self,
|
|
155
|
+
if getattr(self, "setup_" + cmd, self.setup_default)(
|
|
156
|
+
*options
|
|
157
|
+
):
|
|
149
158
|
wrapp_rc = False
|
|
150
159
|
try:
|
|
151
|
-
with self.wrap_actual_child_command(
|
|
160
|
+
with self.wrap_actual_child_command(
|
|
161
|
+
kwoptions
|
|
162
|
+
) as wrapp_rc:
|
|
152
163
|
if wrapp_rc:
|
|
153
|
-
rc = self._actual_child(
|
|
164
|
+
rc = self._actual_child(
|
|
165
|
+
cmd, options, **kwoptions
|
|
166
|
+
)
|
|
154
167
|
else:
|
|
155
|
-
logger.warning(
|
|
168
|
+
logger.warning(
|
|
169
|
+
"Actual [%s %s] command wrap failed",
|
|
170
|
+
self.kind,
|
|
171
|
+
cmd,
|
|
172
|
+
)
|
|
156
173
|
finally:
|
|
157
174
|
if wrapp_rc:
|
|
158
|
-
getattr(
|
|
175
|
+
getattr(
|
|
176
|
+
self, "close_" + cmd, self.close_default
|
|
177
|
+
)(*options)
|
|
159
178
|
else:
|
|
160
|
-
logger.warning(
|
|
161
|
-
|
|
179
|
+
logger.warning(
|
|
180
|
+
"Actual [%s %s] command skipped due to setup action",
|
|
181
|
+
self.kind,
|
|
182
|
+
cmd,
|
|
183
|
+
)
|
|
162
184
|
else:
|
|
163
|
-
logger.warning(
|
|
164
|
-
|
|
185
|
+
logger.warning(
|
|
186
|
+
"Actual [%s %s] command skipped session setup failure",
|
|
187
|
+
self.kind,
|
|
188
|
+
cmd,
|
|
189
|
+
)
|
|
165
190
|
return rc
|
|
166
191
|
|
|
167
192
|
def _actual_child(self, cmd, options, critical=True):
|
|
@@ -182,58 +207,62 @@ class SMS(EcmwfLikeScheduler):
|
|
|
182
207
|
"""
|
|
183
208
|
|
|
184
209
|
_footprint = dict(
|
|
185
|
-
info
|
|
186
|
-
attr
|
|
187
|
-
kind
|
|
188
|
-
values
|
|
210
|
+
info="SMS client service",
|
|
211
|
+
attr=dict(
|
|
212
|
+
kind=dict(
|
|
213
|
+
values=["sms"],
|
|
189
214
|
),
|
|
190
|
-
rootdir
|
|
191
|
-
optional
|
|
192
|
-
default
|
|
193
|
-
alias
|
|
215
|
+
rootdir=dict(
|
|
216
|
+
optional=True,
|
|
217
|
+
default=None,
|
|
218
|
+
alias=("install",),
|
|
194
219
|
),
|
|
195
|
-
env_pattern
|
|
196
|
-
default
|
|
197
|
-
optional
|
|
198
|
-
)
|
|
199
|
-
)
|
|
220
|
+
env_pattern=dict(
|
|
221
|
+
default="SMS",
|
|
222
|
+
optional=True,
|
|
223
|
+
),
|
|
224
|
+
),
|
|
200
225
|
)
|
|
201
226
|
|
|
202
|
-
_KNOWN_CMD = (
|
|
203
|
-
|
|
227
|
+
_KNOWN_CMD = (
|
|
228
|
+
"abort",
|
|
229
|
+
"complete",
|
|
230
|
+
"event",
|
|
231
|
+
"init",
|
|
232
|
+
"label",
|
|
233
|
+
"meter",
|
|
234
|
+
"msg",
|
|
235
|
+
"variable",
|
|
236
|
+
"fix",
|
|
237
|
+
)
|
|
204
238
|
|
|
205
239
|
def __init__(self, *args, **kw):
|
|
206
|
-
logger.debug(
|
|
240
|
+
logger.debug("SMS scheduler client init %s", self)
|
|
207
241
|
super().__init__(*args, **kw)
|
|
208
242
|
self._actual_rootdir = self.rootdir
|
|
209
243
|
if self._actual_rootdir is None:
|
|
210
244
|
self._actual_rootdir = (
|
|
211
|
-
self.env.SMS_INSTALL_ROOT
|
|
212
|
-
from_config(section="sms", key="rootdir")
|
|
245
|
+
self.env.SMS_INSTALL_ROOT
|
|
246
|
+
or config.from_config(section="sms", key="rootdir")
|
|
213
247
|
)
|
|
214
|
-
|
|
215
|
-
logger.warning(
|
|
216
|
-
'SMS service could not guess install location [%s]',
|
|
217
|
-
str(guesspath)
|
|
218
|
-
)
|
|
219
|
-
if self.sh.path.exists(self.cmdpath('init')):
|
|
248
|
+
if self.sh.path.exists(self.cmdpath("init")):
|
|
220
249
|
self.env.setbinpath(self._actual_rootdir)
|
|
221
250
|
else:
|
|
222
251
|
logger.warning(
|
|
223
|
-
|
|
224
|
-
self._actual_rootdir
|
|
252
|
+
"No SMS client found at init time [rootdir:%s]>",
|
|
253
|
+
self._actual_rootdir,
|
|
225
254
|
)
|
|
226
255
|
|
|
227
256
|
def cmd_rename(self, cmd):
|
|
228
257
|
"""Remap command name. Strip any sms prefix."""
|
|
229
258
|
cmd = super().cmd_rename(cmd)
|
|
230
|
-
while cmd.startswith(
|
|
259
|
+
while cmd.startswith("sms"):
|
|
231
260
|
cmd = cmd[3:]
|
|
232
261
|
return cmd
|
|
233
262
|
|
|
234
263
|
def cmdpath(self, cmd):
|
|
235
264
|
"""Return a complete binary path to cmd."""
|
|
236
|
-
cmd =
|
|
265
|
+
cmd = "sms" + self.cmd_rename(cmd)
|
|
237
266
|
if self._actual_rootdir:
|
|
238
267
|
return self.sh.path.join(self._actual_rootdir, cmd)
|
|
239
268
|
else:
|
|
@@ -255,12 +284,12 @@ class SMS(EcmwfLikeScheduler):
|
|
|
255
284
|
"""Last minute wrap before binary child command."""
|
|
256
285
|
with super().wrap_actual_child_command(kwoptions) as wrapp_rc:
|
|
257
286
|
upd_env = dict()
|
|
258
|
-
if not kwoptions.get(
|
|
259
|
-
upd_env[
|
|
287
|
+
if not kwoptions.get("critical", True):
|
|
288
|
+
upd_env["SMSDENIED"] = 1
|
|
260
289
|
if self.non_critical_timeout:
|
|
261
|
-
upd_env[
|
|
290
|
+
upd_env["SMSTIMEOUT"] = self.non_critical_timeout
|
|
262
291
|
if upd_env:
|
|
263
|
-
with self.env.delta_context(**
|
|
292
|
+
with self.env.delta_context(**upd_env):
|
|
264
293
|
yield wrapp_rc
|
|
265
294
|
else:
|
|
266
295
|
yield wrapp_rc
|
|
@@ -278,12 +307,12 @@ class SMSColor(SMS):
|
|
|
278
307
|
"""
|
|
279
308
|
|
|
280
309
|
_footprint = dict(
|
|
281
|
-
info
|
|
282
|
-
attr
|
|
283
|
-
kind
|
|
284
|
-
values
|
|
310
|
+
info="SMS color client service",
|
|
311
|
+
attr=dict(
|
|
312
|
+
kind=dict(
|
|
313
|
+
values=["smscolor"],
|
|
285
314
|
),
|
|
286
|
-
)
|
|
315
|
+
),
|
|
287
316
|
)
|
|
288
317
|
|
|
289
318
|
@contextlib.contextmanager
|
|
@@ -300,28 +329,39 @@ class EcFlow(EcmwfLikeScheduler):
|
|
|
300
329
|
"""
|
|
301
330
|
|
|
302
331
|
_footprint = dict(
|
|
303
|
-
info
|
|
304
|
-
attr
|
|
305
|
-
kind
|
|
306
|
-
values
|
|
332
|
+
info="SMS client service",
|
|
333
|
+
attr=dict(
|
|
334
|
+
kind=dict(
|
|
335
|
+
values=["ecflow"],
|
|
307
336
|
),
|
|
308
|
-
clientpath
|
|
309
|
-
info
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
337
|
+
clientpath=dict(
|
|
338
|
+
info=(
|
|
339
|
+
"Path to the ecFlow client binary (if omitted, "
|
|
340
|
+
+ "it's read in the configuration file)"
|
|
341
|
+
),
|
|
342
|
+
optional=True,
|
|
343
|
+
default=None,
|
|
313
344
|
),
|
|
314
|
-
env_pattern
|
|
315
|
-
default
|
|
316
|
-
optional
|
|
317
|
-
)
|
|
318
|
-
)
|
|
345
|
+
env_pattern=dict(
|
|
346
|
+
default="ECF_",
|
|
347
|
+
optional=True,
|
|
348
|
+
),
|
|
349
|
+
),
|
|
319
350
|
)
|
|
320
351
|
|
|
321
|
-
_KNOWN_CMD = (
|
|
352
|
+
_KNOWN_CMD = (
|
|
353
|
+
"abort",
|
|
354
|
+
"complete",
|
|
355
|
+
"event",
|
|
356
|
+
"init",
|
|
357
|
+
"label",
|
|
358
|
+
"meter",
|
|
359
|
+
"msg",
|
|
360
|
+
"alter",
|
|
361
|
+
)
|
|
322
362
|
|
|
323
363
|
def __init__(self, *args, **kw):
|
|
324
|
-
logger.debug(
|
|
364
|
+
logger.debug("EcFlow scheduler client init %s", self)
|
|
325
365
|
super().__init__(*args, **kw)
|
|
326
366
|
self._actual_clientpath = self.clientpath
|
|
327
367
|
|
|
@@ -329,15 +369,23 @@ class EcFlow(EcmwfLikeScheduler):
|
|
|
329
369
|
"""Return the actual binary path to the EcFlow client."""
|
|
330
370
|
if self._actual_clientpath is None:
|
|
331
371
|
thistarget = self.sh.default_target
|
|
332
|
-
guesspath = self.env.ECF_CLIENT_PATH or thistarget.get(
|
|
333
|
-
|
|
372
|
+
guesspath = self.env.ECF_CLIENT_PATH or thistarget.get(
|
|
373
|
+
"ecflow:clientpath"
|
|
374
|
+
)
|
|
375
|
+
ecfversion = self.env.get("ECF_VERSION", "default")
|
|
334
376
|
guesspath = guesspath.format(version=ecfversion)
|
|
335
377
|
if guesspath is None:
|
|
336
|
-
logger.warning(
|
|
378
|
+
logger.warning(
|
|
379
|
+
"ecFlow service could not guess the install location [%s]",
|
|
380
|
+
str(guesspath),
|
|
381
|
+
)
|
|
337
382
|
else:
|
|
338
383
|
self._actual_clientpath = guesspath
|
|
339
384
|
if not self.sh.path.exists(self._actual_clientpath):
|
|
340
|
-
logger.warning(
|
|
385
|
+
logger.warning(
|
|
386
|
+
"No ecFlow client found at init time [path:%s]>",
|
|
387
|
+
self._actual_clientpath,
|
|
388
|
+
)
|
|
341
389
|
return self._actual_clientpath
|
|
342
390
|
|
|
343
391
|
@contextlib.contextmanager
|
|
@@ -348,24 +396,39 @@ class EcFlow(EcmwfLikeScheduler):
|
|
|
348
396
|
tunnel = None
|
|
349
397
|
# wait and retries from config
|
|
350
398
|
thistarget = self.sh.default_target
|
|
351
|
-
sshwait = float(thistarget.get(
|
|
352
|
-
sshretries = float(
|
|
353
|
-
|
|
399
|
+
sshwait = float(thistarget.get("ecflow:sshproxy_wait", 6))
|
|
400
|
+
sshretries = float(
|
|
401
|
+
thistarget.get("ecflow:sshproxy_retries", 2)
|
|
402
|
+
)
|
|
403
|
+
sshretrydelay = float(
|
|
404
|
+
thistarget.get("ecflow:sshproxy_retrydelay", 1)
|
|
405
|
+
)
|
|
354
406
|
# Build up an SSH tunnel to convey the EcFlow command
|
|
355
407
|
ecconf = self.conf(dict())
|
|
356
|
-
echost = ecconf.get(
|
|
357
|
-
ecport = ecconf.get(
|
|
408
|
+
echost = ecconf.get("{:s}HOST".format(self.env_pattern), None)
|
|
409
|
+
ecport = ecconf.get("{:s}PORT".format(self.env_pattern), None)
|
|
358
410
|
if not (echost and ecport):
|
|
359
411
|
setup_rc = False
|
|
360
412
|
else:
|
|
361
|
-
sshobj = self.sh.ssh(
|
|
362
|
-
|
|
363
|
-
|
|
413
|
+
sshobj = self.sh.ssh(
|
|
414
|
+
"network",
|
|
415
|
+
virtualnode=True,
|
|
416
|
+
mandatory_hostcheck=False,
|
|
417
|
+
maxtries=sshretries,
|
|
418
|
+
triesdelay=sshretrydelay,
|
|
419
|
+
)
|
|
420
|
+
tunnel = sshobj.tunnel(
|
|
421
|
+
echost, int(ecport), maxwait=sshwait
|
|
422
|
+
)
|
|
364
423
|
if not tunnel:
|
|
365
424
|
setup_rc = False
|
|
366
425
|
else:
|
|
367
|
-
newvars = {
|
|
368
|
-
|
|
426
|
+
newvars = {
|
|
427
|
+
"{:s}HOST".format(self.env_pattern): "localhost",
|
|
428
|
+
"{:s}PORT".format(
|
|
429
|
+
self.env_pattern
|
|
430
|
+
): tunnel.entranceport,
|
|
431
|
+
}
|
|
369
432
|
self.env.update(**newvars)
|
|
370
433
|
try:
|
|
371
434
|
yield setup_rc
|
|
@@ -381,27 +444,31 @@ class EcFlow(EcmwfLikeScheduler):
|
|
|
381
444
|
"""Last minute wrap before binary child command."""
|
|
382
445
|
with super().wrap_actual_child_command(kwoptions) as wrapp_rc:
|
|
383
446
|
upd_env = dict()
|
|
384
|
-
if not kwoptions.get(
|
|
385
|
-
upd_env[
|
|
447
|
+
if not kwoptions.get("critical", True):
|
|
448
|
+
upd_env["{:s}DENIED".format(self.env_pattern)] = 1
|
|
386
449
|
if self.non_critical_timeout:
|
|
387
|
-
upd_env[
|
|
450
|
+
upd_env["{:s}TIMEOUT".format(self.env_pattern)] = (
|
|
451
|
+
self.non_critical_timeout
|
|
452
|
+
)
|
|
388
453
|
if upd_env:
|
|
389
|
-
with self.env.delta_context(**
|
|
454
|
+
with self.env.delta_context(**upd_env):
|
|
390
455
|
yield wrapp_rc
|
|
391
456
|
else:
|
|
392
457
|
yield wrapp_rc
|
|
393
458
|
|
|
394
459
|
def _actual_child(self, cmd, options, critical=True):
|
|
395
460
|
"""Miscellaneous ecFlow sub-command."""
|
|
396
|
-
args = [
|
|
461
|
+
args = [
|
|
462
|
+
self.path(),
|
|
463
|
+
]
|
|
397
464
|
if options:
|
|
398
|
-
args.append(
|
|
465
|
+
args.append("--{:s}={!s}".format(cmd, options[0]))
|
|
399
466
|
if len(options) > 1:
|
|
400
467
|
args.extend(options[1:])
|
|
401
468
|
else:
|
|
402
|
-
args.append(
|
|
469
|
+
args.append("--{:s}".format(cmd))
|
|
403
470
|
args = [str(a) for a in args]
|
|
404
|
-
logger.info(
|
|
471
|
+
logger.info("Issuing the ecFlow command: %s", " ".join(args[1:]))
|
|
405
472
|
return self.sh.spawn(args, output=False, fatal=critical)
|
|
406
473
|
|
|
407
474
|
def abort(self, *opts):
|
|
@@ -410,4 +477,4 @@ class EcFlow(EcmwfLikeScheduler):
|
|
|
410
477
|
if not actual_opts:
|
|
411
478
|
# For backward compatibility with SMS
|
|
412
479
|
actual_opts.append("No abort reason provided")
|
|
413
|
-
return self.child(
|
|
480
|
+
return self.child("abort", *actual_opts)
|