vortex-nwp 2.0.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 +159 -0
- vortex/algo/__init__.py +13 -0
- vortex/algo/components.py +2462 -0
- vortex/algo/mpitools.py +1953 -0
- vortex/algo/mpitools_templates/__init__.py +1 -0
- vortex/algo/mpitools_templates/envelope_wrapper_default.tpl +27 -0
- vortex/algo/mpitools_templates/envelope_wrapper_mpiauto.tpl +29 -0
- vortex/algo/mpitools_templates/wrapstd_wrapper_default.tpl +18 -0
- vortex/algo/serversynctools.py +171 -0
- vortex/config.py +112 -0
- vortex/data/__init__.py +19 -0
- vortex/data/abstractstores.py +1510 -0
- vortex/data/containers.py +835 -0
- vortex/data/contents.py +622 -0
- vortex/data/executables.py +275 -0
- vortex/data/flow.py +119 -0
- vortex/data/geometries.ini +2689 -0
- vortex/data/geometries.py +799 -0
- vortex/data/handlers.py +1230 -0
- vortex/data/outflow.py +67 -0
- vortex/data/providers.py +487 -0
- vortex/data/resources.py +207 -0
- vortex/data/stores.py +1390 -0
- vortex/data/sync_templates/__init__.py +0 -0
- vortex/gloves.py +309 -0
- vortex/layout/__init__.py +20 -0
- vortex/layout/contexts.py +577 -0
- vortex/layout/dataflow.py +1220 -0
- vortex/layout/monitor.py +969 -0
- vortex/nwp/__init__.py +14 -0
- vortex/nwp/algo/__init__.py +21 -0
- vortex/nwp/algo/assim.py +537 -0
- vortex/nwp/algo/clim.py +1086 -0
- vortex/nwp/algo/coupling.py +831 -0
- vortex/nwp/algo/eda.py +840 -0
- vortex/nwp/algo/eps.py +785 -0
- vortex/nwp/algo/forecasts.py +886 -0
- vortex/nwp/algo/fpserver.py +1303 -0
- vortex/nwp/algo/ifsnaming.py +463 -0
- vortex/nwp/algo/ifsroot.py +404 -0
- vortex/nwp/algo/monitoring.py +263 -0
- vortex/nwp/algo/mpitools.py +694 -0
- vortex/nwp/algo/odbtools.py +1258 -0
- vortex/nwp/algo/oopsroot.py +916 -0
- vortex/nwp/algo/oopstests.py +220 -0
- vortex/nwp/algo/request.py +660 -0
- vortex/nwp/algo/stdpost.py +1641 -0
- vortex/nwp/data/__init__.py +30 -0
- vortex/nwp/data/assim.py +380 -0
- vortex/nwp/data/boundaries.py +314 -0
- vortex/nwp/data/climfiles.py +521 -0
- vortex/nwp/data/configfiles.py +153 -0
- vortex/nwp/data/consts.py +954 -0
- vortex/nwp/data/ctpini.py +149 -0
- vortex/nwp/data/diagnostics.py +209 -0
- vortex/nwp/data/eda.py +147 -0
- vortex/nwp/data/eps.py +432 -0
- vortex/nwp/data/executables.py +1045 -0
- vortex/nwp/data/fields.py +111 -0
- vortex/nwp/data/gridfiles.py +380 -0
- vortex/nwp/data/logs.py +584 -0
- vortex/nwp/data/modelstates.py +363 -0
- vortex/nwp/data/monitoring.py +193 -0
- vortex/nwp/data/namelists.py +696 -0
- vortex/nwp/data/obs.py +840 -0
- vortex/nwp/data/oopsexec.py +74 -0
- vortex/nwp/data/providers.py +207 -0
- vortex/nwp/data/query.py +206 -0
- vortex/nwp/data/stores.py +160 -0
- vortex/nwp/data/surfex.py +337 -0
- vortex/nwp/syntax/__init__.py +9 -0
- vortex/nwp/syntax/stdattrs.py +437 -0
- vortex/nwp/tools/__init__.py +10 -0
- vortex/nwp/tools/addons.py +40 -0
- vortex/nwp/tools/agt.py +67 -0
- vortex/nwp/tools/bdap.py +59 -0
- vortex/nwp/tools/bdcp.py +41 -0
- vortex/nwp/tools/bdm.py +24 -0
- vortex/nwp/tools/bdmp.py +54 -0
- vortex/nwp/tools/conftools.py +1661 -0
- vortex/nwp/tools/drhook.py +66 -0
- vortex/nwp/tools/grib.py +294 -0
- vortex/nwp/tools/gribdiff.py +104 -0
- vortex/nwp/tools/ifstools.py +203 -0
- vortex/nwp/tools/igastuff.py +273 -0
- vortex/nwp/tools/mars.py +68 -0
- vortex/nwp/tools/odb.py +657 -0
- vortex/nwp/tools/partitioning.py +258 -0
- vortex/nwp/tools/satrad.py +71 -0
- vortex/nwp/util/__init__.py +6 -0
- vortex/nwp/util/async.py +212 -0
- vortex/nwp/util/beacon.py +40 -0
- vortex/nwp/util/diffpygram.py +447 -0
- vortex/nwp/util/ens.py +279 -0
- vortex/nwp/util/hooks.py +139 -0
- vortex/nwp/util/taskdeco.py +85 -0
- vortex/nwp/util/usepygram.py +697 -0
- vortex/nwp/util/usetnt.py +101 -0
- vortex/proxy.py +6 -0
- vortex/sessions.py +374 -0
- vortex/syntax/__init__.py +9 -0
- vortex/syntax/stdattrs.py +867 -0
- vortex/syntax/stddeco.py +185 -0
- vortex/toolbox.py +1117 -0
- vortex/tools/__init__.py +20 -0
- vortex/tools/actions.py +523 -0
- vortex/tools/addons.py +316 -0
- vortex/tools/arm.py +96 -0
- vortex/tools/compression.py +325 -0
- vortex/tools/date.py +27 -0
- vortex/tools/ddhpack.py +10 -0
- vortex/tools/delayedactions.py +782 -0
- vortex/tools/env.py +541 -0
- vortex/tools/folder.py +834 -0
- vortex/tools/grib.py +738 -0
- vortex/tools/lfi.py +953 -0
- vortex/tools/listings.py +423 -0
- vortex/tools/names.py +637 -0
- vortex/tools/net.py +2124 -0
- vortex/tools/odb.py +10 -0
- vortex/tools/parallelism.py +368 -0
- vortex/tools/prestaging.py +210 -0
- vortex/tools/rawfiles.py +10 -0
- vortex/tools/schedulers.py +480 -0
- vortex/tools/services.py +940 -0
- vortex/tools/storage.py +996 -0
- vortex/tools/surfex.py +61 -0
- vortex/tools/systems.py +3976 -0
- vortex/tools/targets.py +440 -0
- vortex/util/__init__.py +9 -0
- vortex/util/config.py +1122 -0
- vortex/util/empty.py +24 -0
- vortex/util/helpers.py +216 -0
- vortex/util/introspection.py +69 -0
- vortex/util/iosponge.py +80 -0
- vortex/util/roles.py +49 -0
- vortex/util/storefunctions.py +129 -0
- vortex/util/structs.py +26 -0
- vortex/util/worker.py +162 -0
- vortex_nwp-2.0.0.dist-info/METADATA +67 -0
- vortex_nwp-2.0.0.dist-info/RECORD +144 -0
- vortex_nwp-2.0.0.dist-info/WHEEL +5 -0
- vortex_nwp-2.0.0.dist-info/licenses/LICENSE +517 -0
- vortex_nwp-2.0.0.dist-info/top_level.txt +1 -0
vortex/tools/lfi.py
ADDED
|
@@ -0,0 +1,953 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module needed to interact with FA and LFI files.
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
It provides shell addons to deal with:
|
|
6
|
+
|
|
7
|
+
* Splitted FA files (as produced by the Arpege/IFS IO server)
|
|
8
|
+
* The ability to compare Fa or LFI files
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import re
|
|
13
|
+
|
|
14
|
+
import footprints
|
|
15
|
+
|
|
16
|
+
from bronx.fancies import loggers
|
|
17
|
+
from bronx.stdtypes.tracking import Tracker
|
|
18
|
+
|
|
19
|
+
from . import addons, systems
|
|
20
|
+
|
|
21
|
+
from vortex.layout import contexts
|
|
22
|
+
from vortex.tools.net import DEFAULT_FTP_PORT
|
|
23
|
+
|
|
24
|
+
#: Export nothing
|
|
25
|
+
__all__ = []
|
|
26
|
+
|
|
27
|
+
logger = loggers.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def use_in_shell(sh, **kw):
|
|
31
|
+
"""Extend current shell with the LFI interface defined by optional arguments."""
|
|
32
|
+
kw["shell"] = sh
|
|
33
|
+
return footprints.proxy.addon(**kw)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class LFI_Status:
|
|
37
|
+
"""
|
|
38
|
+
Store lfi commands status as a set of attributes:
|
|
39
|
+
* rc = return code
|
|
40
|
+
* stdout = raw standard output
|
|
41
|
+
* result = an optional processing specific to each command
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(self, rc=0, ok=None, stdout=None, stderr=None, result=None):
|
|
45
|
+
self._rc = rc
|
|
46
|
+
self._ok = ok or [0]
|
|
47
|
+
self._stdout = stdout
|
|
48
|
+
self._stderr = stderr
|
|
49
|
+
self._result = result or list()
|
|
50
|
+
|
|
51
|
+
def __str__(self):
|
|
52
|
+
return "{:s} | rc={:d} result={:d}>".format(
|
|
53
|
+
repr(self).rstrip(">"), self.rc, len(self.result)
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def ok(self):
|
|
58
|
+
return self._ok
|
|
59
|
+
|
|
60
|
+
def _get_rc(self):
|
|
61
|
+
return self._rc
|
|
62
|
+
|
|
63
|
+
def _set_rc(self, value):
|
|
64
|
+
if value is not None:
|
|
65
|
+
if type(value) is bool:
|
|
66
|
+
value = 1 - int(value)
|
|
67
|
+
self._rc = self._rc + value
|
|
68
|
+
|
|
69
|
+
rc = property(_get_rc, _set_rc, None, None)
|
|
70
|
+
|
|
71
|
+
def _get_stdout(self):
|
|
72
|
+
return self._stdout
|
|
73
|
+
|
|
74
|
+
def _set_stdout(self, value):
|
|
75
|
+
self._stdout = list(value)
|
|
76
|
+
|
|
77
|
+
stdout = property(_get_stdout, _set_stdout, None, None)
|
|
78
|
+
|
|
79
|
+
def _get_stderr(self):
|
|
80
|
+
return self._stderr
|
|
81
|
+
|
|
82
|
+
def _set_stderr(self, value):
|
|
83
|
+
self._stderr = list(value)
|
|
84
|
+
|
|
85
|
+
stderr = property(_get_stderr, _set_stderr, None, None)
|
|
86
|
+
|
|
87
|
+
def _get_result(self):
|
|
88
|
+
return self._result
|
|
89
|
+
|
|
90
|
+
def _set_result(self, value):
|
|
91
|
+
self._result = list(value)
|
|
92
|
+
|
|
93
|
+
result = property(_get_result, _set_result, None, None)
|
|
94
|
+
|
|
95
|
+
def cat(self, maxlines=None):
|
|
96
|
+
"""Cat the last stdout command up to ``maxlines`` lines. If maxlines is None, print all."""
|
|
97
|
+
if self.stdout is not None:
|
|
98
|
+
if maxlines is None:
|
|
99
|
+
maxlines = len(self.stdout) + 1
|
|
100
|
+
for l in self.stdout[:maxlines]:
|
|
101
|
+
print(l)
|
|
102
|
+
|
|
103
|
+
def __bool__(self):
|
|
104
|
+
return bool(self.rc in self.ok)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class LFI_Tool_Raw(addons.FtrawEnableAddon):
|
|
108
|
+
"""
|
|
109
|
+
Interface to LFI commands through Perl wrappers.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
LFI_HNDL_SPEC = ":1"
|
|
113
|
+
DR_HOOK_SILENT = 1
|
|
114
|
+
DR_HOOK_NOT_MPI = 1
|
|
115
|
+
DR_HOOK_ASSERT_MPI_INITIALIZED = 0
|
|
116
|
+
OMP_STACKSIZE = "32M"
|
|
117
|
+
KMP_STACKSIZE = "32M"
|
|
118
|
+
KMP_MONITOR_STACKSIZE = "32M"
|
|
119
|
+
|
|
120
|
+
_footprint = dict(
|
|
121
|
+
info="Default LFI system interface (Perl)",
|
|
122
|
+
attr=dict(
|
|
123
|
+
kind=dict(
|
|
124
|
+
values=["lfi"],
|
|
125
|
+
),
|
|
126
|
+
cmd=dict(
|
|
127
|
+
alias=("lficmd",),
|
|
128
|
+
default="lfitools",
|
|
129
|
+
),
|
|
130
|
+
path=dict(
|
|
131
|
+
alias=("lfipath",),
|
|
132
|
+
),
|
|
133
|
+
warnpack=dict(
|
|
134
|
+
type=bool,
|
|
135
|
+
optional=True,
|
|
136
|
+
default=False,
|
|
137
|
+
),
|
|
138
|
+
wraplanguage=dict(
|
|
139
|
+
values=[
|
|
140
|
+
"perl",
|
|
141
|
+
],
|
|
142
|
+
default="perl",
|
|
143
|
+
optional=True,
|
|
144
|
+
doc_visibility=footprints.doc.visibility.ADVANCED,
|
|
145
|
+
),
|
|
146
|
+
toolkind=dict(
|
|
147
|
+
default="lfitools",
|
|
148
|
+
),
|
|
149
|
+
),
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
def __init__(self, *args, **kw):
|
|
153
|
+
"""LFI tools initialisation."""
|
|
154
|
+
super().__init__(*args, **kw)
|
|
155
|
+
self._lfitools_path = dict()
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def lfitools_path(self):
|
|
159
|
+
ctxtag = contexts.Context.tag_focus()
|
|
160
|
+
if ctxtag not in self._lfitools_path:
|
|
161
|
+
self._lfitools_path[ctxtag] = self.sh.path.join(
|
|
162
|
+
self.actual_path, self.actual_cmd
|
|
163
|
+
)
|
|
164
|
+
self.sh.xperm(self._lfitools_path[ctxtag], force=True)
|
|
165
|
+
return self._lfitools_path[ctxtag]
|
|
166
|
+
|
|
167
|
+
def _spawn(self, cmd, **kw):
|
|
168
|
+
"""Tube to set LFITOOLS env variable."""
|
|
169
|
+
self.env.LFITOOLS = self.lfitools_path
|
|
170
|
+
return super()._spawn(cmd, **kw)
|
|
171
|
+
|
|
172
|
+
def _spawn_wrap(self, func, cmd, **kw):
|
|
173
|
+
"""Tube to set LFITOOLS env variable."""
|
|
174
|
+
self.env.LFITOOLS = self.lfitools_path
|
|
175
|
+
return super()._spawn_wrap(
|
|
176
|
+
[
|
|
177
|
+
"lfi_" + func,
|
|
178
|
+
]
|
|
179
|
+
+ cmd,
|
|
180
|
+
**kw,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
def is_xlfi(self, source):
|
|
184
|
+
"""Check if the given ``source`` is a multipart-lfi file."""
|
|
185
|
+
rc = False
|
|
186
|
+
if source and isinstance(source, str) and self.sh.path.exists(source):
|
|
187
|
+
with open(source, "rb") as fd:
|
|
188
|
+
rc = fd.read(8) == b"LFI_ALTM"
|
|
189
|
+
return rc
|
|
190
|
+
|
|
191
|
+
def _std_table(self, lfifile, **kw):
|
|
192
|
+
"""
|
|
193
|
+
List of contents of a lfi-file.
|
|
194
|
+
|
|
195
|
+
Mandatory args are:
|
|
196
|
+
* lfifile : lfi file name
|
|
197
|
+
|
|
198
|
+
"""
|
|
199
|
+
cmd = ["lfilist", lfifile]
|
|
200
|
+
kw["output"] = True
|
|
201
|
+
rawout = self._spawn(cmd, **kw)
|
|
202
|
+
return LFI_Status(
|
|
203
|
+
rc=0,
|
|
204
|
+
stdout=rawout,
|
|
205
|
+
result=[tuple(eval(x)[0]) for x in rawout if x.startswith("[")],
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
fa_table = lfi_table = _std_table
|
|
209
|
+
|
|
210
|
+
def _std_diff(self, lfi1, lfi2, **kw):
|
|
211
|
+
"""
|
|
212
|
+
Difference between two lfi-files.
|
|
213
|
+
|
|
214
|
+
Mandatory args are:
|
|
215
|
+
* lfi1 : first file to compare
|
|
216
|
+
* lfi2 : second file to compare
|
|
217
|
+
|
|
218
|
+
Options are:
|
|
219
|
+
* maxprint : Maximum number of values to print
|
|
220
|
+
* skipfields : LFI fields not to be compared
|
|
221
|
+
* skiplength : Offset at which the comparison starts for each LFI fields
|
|
222
|
+
"""
|
|
223
|
+
cmd = ["lfidiff", "--lfi-file-1", lfi1, "--lfi-file-2", lfi2]
|
|
224
|
+
|
|
225
|
+
maxprint = kw.pop("maxprint", 2)
|
|
226
|
+
if maxprint:
|
|
227
|
+
cmd.extend(["--max-print-diff", str(maxprint)])
|
|
228
|
+
|
|
229
|
+
skipfields = kw.pop("skipfields", 0)
|
|
230
|
+
if skipfields:
|
|
231
|
+
cmd.extend(["--lfi-skip-fields", str(skipfields)])
|
|
232
|
+
|
|
233
|
+
skiplength = kw.pop("skiplength", 0)
|
|
234
|
+
if skiplength:
|
|
235
|
+
cmd.extend(["--lfi-skip-length", str(skiplength)])
|
|
236
|
+
|
|
237
|
+
kw["output"] = True
|
|
238
|
+
|
|
239
|
+
rawout = self._spawn(cmd, **kw)
|
|
240
|
+
fields = [
|
|
241
|
+
tuple(x.split(" ", 2)[-2:])
|
|
242
|
+
for x in rawout
|
|
243
|
+
if re.match(r" (?:\!=|\+\+|\-\-)", x)
|
|
244
|
+
]
|
|
245
|
+
|
|
246
|
+
trfields = Tracker(
|
|
247
|
+
deleted=[x[1] for x in fields if x[0] == "--"],
|
|
248
|
+
created=[x[1] for x in fields if x[0] == "++"],
|
|
249
|
+
updated=[x[1] for x in fields if x[0] == "!="],
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
stlist = self.lfi_table(lfi1, output=True)
|
|
253
|
+
trfields.unchanged = {x[0] for x in stlist.result} - set(trfields)
|
|
254
|
+
|
|
255
|
+
return LFI_Status(rc=int(bool(fields)), stdout=rawout, result=trfields)
|
|
256
|
+
|
|
257
|
+
fa_diff = lfi_diff = _std_diff
|
|
258
|
+
|
|
259
|
+
def fa_empty(self, fa1, fa2, **kw):
|
|
260
|
+
"""
|
|
261
|
+
Create an empty FA file
|
|
262
|
+
|
|
263
|
+
Mandatory args are:
|
|
264
|
+
* fa1 : The reference file
|
|
265
|
+
* fa2 : The new empty file
|
|
266
|
+
|
|
267
|
+
"""
|
|
268
|
+
cmd = ["faempty", fa1, fa2]
|
|
269
|
+
self._spawn(cmd, **kw)
|
|
270
|
+
|
|
271
|
+
def _pack_stream(self, source):
|
|
272
|
+
return self._spawn_wrap(
|
|
273
|
+
"pack",
|
|
274
|
+
[
|
|
275
|
+
source,
|
|
276
|
+
],
|
|
277
|
+
output=False,
|
|
278
|
+
inpipe=True,
|
|
279
|
+
bufsize=8192,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
def _packed_size(self, source):
|
|
283
|
+
out = self._spawn_wrap(
|
|
284
|
+
"size",
|
|
285
|
+
[
|
|
286
|
+
source,
|
|
287
|
+
],
|
|
288
|
+
output=True,
|
|
289
|
+
inpipe=False,
|
|
290
|
+
)
|
|
291
|
+
try:
|
|
292
|
+
return int(out[0])
|
|
293
|
+
except ValueError:
|
|
294
|
+
pass
|
|
295
|
+
return None
|
|
296
|
+
|
|
297
|
+
def _std_forcepack(self, source, destination=None):
|
|
298
|
+
"""Returned a path to a packed data."""
|
|
299
|
+
if self.is_xlfi(source):
|
|
300
|
+
destination = (
|
|
301
|
+
destination
|
|
302
|
+
if destination
|
|
303
|
+
else self.sh.safe_fileaddsuffix(source)
|
|
304
|
+
)
|
|
305
|
+
if not self.sh.path.exists(destination):
|
|
306
|
+
st = self._std_copy(
|
|
307
|
+
source=source, destination=destination, pack=True
|
|
308
|
+
)
|
|
309
|
+
if st:
|
|
310
|
+
return destination
|
|
311
|
+
else:
|
|
312
|
+
raise OSError("XLFI packing failed")
|
|
313
|
+
else:
|
|
314
|
+
return destination
|
|
315
|
+
else:
|
|
316
|
+
return source
|
|
317
|
+
|
|
318
|
+
fa_forcepack = lfi_forcepack = _std_forcepack
|
|
319
|
+
|
|
320
|
+
def _std_ftput(
|
|
321
|
+
self,
|
|
322
|
+
source,
|
|
323
|
+
destination,
|
|
324
|
+
hostname=None,
|
|
325
|
+
logname=None,
|
|
326
|
+
port=DEFAULT_FTP_PORT,
|
|
327
|
+
cpipeline=None,
|
|
328
|
+
sync=False,
|
|
329
|
+
):
|
|
330
|
+
"""On the fly packing and ftp."""
|
|
331
|
+
if self.is_xlfi(source):
|
|
332
|
+
if cpipeline is not None:
|
|
333
|
+
raise OSError("It's not allowed to compress xlfi files.")
|
|
334
|
+
hostname = self.sh.fix_fthostname(hostname)
|
|
335
|
+
|
|
336
|
+
st = LFI_Status()
|
|
337
|
+
ftp = self.sh.ftp(hostname, logname, port=port)
|
|
338
|
+
if ftp:
|
|
339
|
+
packed_size = self._packed_size(source)
|
|
340
|
+
p = self._pack_stream(source)
|
|
341
|
+
st.rc = ftp.put(
|
|
342
|
+
p.stdout, destination, size=packed_size, exact=True
|
|
343
|
+
)
|
|
344
|
+
self.sh.pclose(p)
|
|
345
|
+
st.result = [destination]
|
|
346
|
+
st.stdout = [
|
|
347
|
+
"Connection time : {:f}".format(ftp.length),
|
|
348
|
+
"Packed source size: {:d}".format(packed_size),
|
|
349
|
+
"Actual target size: {:d}".format(ftp.size(destination)),
|
|
350
|
+
]
|
|
351
|
+
ftp.close()
|
|
352
|
+
else:
|
|
353
|
+
st.rc = 1
|
|
354
|
+
st.result = [
|
|
355
|
+
"Could not connect to " + hostname + " as user " + logname
|
|
356
|
+
]
|
|
357
|
+
return st
|
|
358
|
+
else:
|
|
359
|
+
return self.sh.ftput(
|
|
360
|
+
source,
|
|
361
|
+
destination,
|
|
362
|
+
hostname=hostname,
|
|
363
|
+
logname=logname,
|
|
364
|
+
cpipeline=cpipeline,
|
|
365
|
+
port=port,
|
|
366
|
+
sync=sync,
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
def _std_rawftput(
|
|
370
|
+
self,
|
|
371
|
+
source,
|
|
372
|
+
destination,
|
|
373
|
+
hostname=None,
|
|
374
|
+
logname=None,
|
|
375
|
+
port=None,
|
|
376
|
+
cpipeline=None,
|
|
377
|
+
sync=False,
|
|
378
|
+
):
|
|
379
|
+
"""Use ftserv as much as possible."""
|
|
380
|
+
if self.is_xlfi(source):
|
|
381
|
+
if cpipeline is not None:
|
|
382
|
+
raise OSError("It's not allowed to compress xlfi files.")
|
|
383
|
+
if self.sh.ftraw and self.rawftshell is not None:
|
|
384
|
+
newsource = self.sh.copy2ftspool(source, fmt="lfi")
|
|
385
|
+
rc = self.sh.ftserv_put(
|
|
386
|
+
newsource,
|
|
387
|
+
destination,
|
|
388
|
+
hostname=hostname,
|
|
389
|
+
logname=logname,
|
|
390
|
+
port=port,
|
|
391
|
+
specialshell=self.rawftshell,
|
|
392
|
+
sync=sync,
|
|
393
|
+
)
|
|
394
|
+
self.sh.rm(newsource) # Delete the request file
|
|
395
|
+
return rc
|
|
396
|
+
else:
|
|
397
|
+
if port is None:
|
|
398
|
+
port = DEFAULT_FTP_PORT
|
|
399
|
+
return self._std_ftput(
|
|
400
|
+
source,
|
|
401
|
+
destination,
|
|
402
|
+
hostname,
|
|
403
|
+
logname,
|
|
404
|
+
port=port,
|
|
405
|
+
sync=sync,
|
|
406
|
+
)
|
|
407
|
+
else:
|
|
408
|
+
return self.sh.rawftput(
|
|
409
|
+
source,
|
|
410
|
+
destination,
|
|
411
|
+
hostname=hostname,
|
|
412
|
+
logname=logname,
|
|
413
|
+
port=port,
|
|
414
|
+
cpipeline=cpipeline,
|
|
415
|
+
sync=sync,
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
fa_ftput = lfi_ftput = _std_ftput
|
|
419
|
+
fa_rawftput = lfi_rawftput = _std_rawftput
|
|
420
|
+
|
|
421
|
+
def _std_prepare(self, source, destination, intent="in"):
|
|
422
|
+
"""Check for the source and prepare the destination."""
|
|
423
|
+
if intent not in ("in", "inout"):
|
|
424
|
+
raise ValueError("Incorrect value for intent ({})".format(intent))
|
|
425
|
+
st = LFI_Status()
|
|
426
|
+
if not self.sh.path.exists(source):
|
|
427
|
+
logger.error("Missing source %s", source)
|
|
428
|
+
st.rc = 2
|
|
429
|
+
st.stderr = "No such source file or directory : [" + source + "]"
|
|
430
|
+
return st
|
|
431
|
+
if not self.sh.filecocoon(destination):
|
|
432
|
+
raise OSError("Could not cocoon [" + destination + "]")
|
|
433
|
+
if not self.lfi_rm(destination):
|
|
434
|
+
raise OSError("Could not clean destination [" + destination + "]")
|
|
435
|
+
return st
|
|
436
|
+
|
|
437
|
+
def _std_remove(self, *args):
|
|
438
|
+
"""Remove (possibly) multi lfi files."""
|
|
439
|
+
st = LFI_Status(result=list())
|
|
440
|
+
for pname in args:
|
|
441
|
+
for objpath in self.sh.glob(pname):
|
|
442
|
+
rc = self._spawn_wrap(
|
|
443
|
+
"remove",
|
|
444
|
+
[
|
|
445
|
+
objpath,
|
|
446
|
+
],
|
|
447
|
+
output=False,
|
|
448
|
+
)
|
|
449
|
+
st.result.append(
|
|
450
|
+
dict(path=objpath, multi=self.is_xlfi(objpath), rc=rc)
|
|
451
|
+
)
|
|
452
|
+
st.rc = rc
|
|
453
|
+
return st
|
|
454
|
+
|
|
455
|
+
lfi_rm = lfi_remove = fa_rm = fa_remove = _std_remove
|
|
456
|
+
|
|
457
|
+
def _std_copy(
|
|
458
|
+
self,
|
|
459
|
+
source,
|
|
460
|
+
destination,
|
|
461
|
+
smartcp_threshold=0,
|
|
462
|
+
intent="in",
|
|
463
|
+
pack=False,
|
|
464
|
+
silent=False,
|
|
465
|
+
):
|
|
466
|
+
"""Extended copy for (possibly) multi lfi file."""
|
|
467
|
+
st = self._std_prepare(source, destination, intent)
|
|
468
|
+
if st.rc == 0:
|
|
469
|
+
ln_same_fs = self.sh.is_samefs(source, destination)
|
|
470
|
+
ln_cross_users = ln_same_fs and not self.sh.usr_file(source)
|
|
471
|
+
actual_pack = pack
|
|
472
|
+
actual_intent = intent
|
|
473
|
+
if ln_cross_users and not self.sh.allow_cross_users_links:
|
|
474
|
+
actual_pack = True
|
|
475
|
+
actual_intent = "inout"
|
|
476
|
+
try:
|
|
477
|
+
st.rc = self._spawn_wrap(
|
|
478
|
+
"copy",
|
|
479
|
+
(
|
|
480
|
+
[
|
|
481
|
+
"-pack",
|
|
482
|
+
]
|
|
483
|
+
if actual_pack
|
|
484
|
+
else []
|
|
485
|
+
)
|
|
486
|
+
+ [
|
|
487
|
+
"-intent={}".format(actual_intent),
|
|
488
|
+
source,
|
|
489
|
+
destination,
|
|
490
|
+
],
|
|
491
|
+
output=False,
|
|
492
|
+
)
|
|
493
|
+
except systems.ExecutionError:
|
|
494
|
+
if self.sh.allow_cross_users_links and ln_cross_users:
|
|
495
|
+
# This is expected to fail if the fs.protected_hardlinks
|
|
496
|
+
# Linux kernel setting is 1.
|
|
497
|
+
logger.info(
|
|
498
|
+
"Force System's allow_cross_users_links to False"
|
|
499
|
+
)
|
|
500
|
+
self.sh.allow_cross_users_links = False
|
|
501
|
+
logger.info("Re-running the cp command on this LFI file")
|
|
502
|
+
st = self._std_copy(
|
|
503
|
+
source,
|
|
504
|
+
destination,
|
|
505
|
+
intent=intent,
|
|
506
|
+
pack=pack,
|
|
507
|
+
silent=silent,
|
|
508
|
+
)
|
|
509
|
+
else:
|
|
510
|
+
raise
|
|
511
|
+
else:
|
|
512
|
+
if (
|
|
513
|
+
ln_cross_users
|
|
514
|
+
and not self.sh.allow_cross_users_links
|
|
515
|
+
and intent == "in"
|
|
516
|
+
):
|
|
517
|
+
self.sh.readonly(destination)
|
|
518
|
+
return st
|
|
519
|
+
|
|
520
|
+
lfi_cp = lfi_copy = fa_cp = fa_copy = _std_copy
|
|
521
|
+
|
|
522
|
+
def _std_move(self, source, destination, intent=None, pack=False):
|
|
523
|
+
"""Extended mv for (possibly) multi lfi file."""
|
|
524
|
+
if self.is_xlfi(source):
|
|
525
|
+
if intent is None:
|
|
526
|
+
intent = (
|
|
527
|
+
"inout" if self.sh.access(source, self.sh.W_OK) else "in"
|
|
528
|
+
)
|
|
529
|
+
st = self._std_prepare(source, destination, intent)
|
|
530
|
+
if st.rc == 0:
|
|
531
|
+
st.rc = self._spawn_wrap(
|
|
532
|
+
"move",
|
|
533
|
+
(
|
|
534
|
+
[
|
|
535
|
+
"-pack",
|
|
536
|
+
]
|
|
537
|
+
if pack
|
|
538
|
+
else []
|
|
539
|
+
)
|
|
540
|
+
+ ["-intent={}".format(intent), source, destination],
|
|
541
|
+
output=False,
|
|
542
|
+
)
|
|
543
|
+
else:
|
|
544
|
+
st = LFI_Status()
|
|
545
|
+
st.rc = self.sh.mv(source, destination)
|
|
546
|
+
return st
|
|
547
|
+
|
|
548
|
+
lfi_mv = lfi_move = fa_mv = fa_move = _std_move
|
|
549
|
+
|
|
550
|
+
def _std_scpput(
|
|
551
|
+
self, source, destination, hostname, logname=None, cpipeline=None
|
|
552
|
+
):
|
|
553
|
+
"""On the fly packing and scp."""
|
|
554
|
+
if not self.is_xlfi(source):
|
|
555
|
+
rc = self.sh.scpput(
|
|
556
|
+
source, destination, hostname, logname, cpipeline
|
|
557
|
+
)
|
|
558
|
+
else:
|
|
559
|
+
if cpipeline is not None:
|
|
560
|
+
raise OSError("It's not allowed to compress xlfi files.")
|
|
561
|
+
logname = self.sh.fix_ftuser(
|
|
562
|
+
hostname, logname, fatal=False, defaults_to_user=False
|
|
563
|
+
)
|
|
564
|
+
ssh = self.sh.ssh(hostname, logname)
|
|
565
|
+
permissions = ssh.get_permissions(source)
|
|
566
|
+
# remove the .d companion directory (scp_stream removes the destination)
|
|
567
|
+
# go on on failure : the .d lingers on, but the lfi will be self-contained
|
|
568
|
+
ssh.remove(destination + ".d")
|
|
569
|
+
p = self._pack_stream(source)
|
|
570
|
+
rc = ssh.scpput_stream(
|
|
571
|
+
p.stdout, destination, permissions=permissions
|
|
572
|
+
)
|
|
573
|
+
self.sh.pclose(p)
|
|
574
|
+
return rc
|
|
575
|
+
|
|
576
|
+
fa_scpput = lfi_scpput = _std_scpput
|
|
577
|
+
|
|
578
|
+
@addons.require_external_addon("ecfs")
|
|
579
|
+
def _std_ecfsput(self, source, target, cpipeline=None, options=None):
|
|
580
|
+
"""
|
|
581
|
+
:param source: source file
|
|
582
|
+
:param target: target file
|
|
583
|
+
:param cpipeline: compression pipeline to be used, if provided
|
|
584
|
+
:param options: list of options to be used
|
|
585
|
+
:return: return code and additional attributes used
|
|
586
|
+
"""
|
|
587
|
+
if self.is_xlfi(source):
|
|
588
|
+
if cpipeline is not None:
|
|
589
|
+
raise OSError("It's not allowed to compress xlfi files.")
|
|
590
|
+
psource = self.sh.safe_fileaddsuffix(source)
|
|
591
|
+
rc = LFI_Status()
|
|
592
|
+
try:
|
|
593
|
+
st = self._std_copy(
|
|
594
|
+
source=source, destination=psource, pack=True
|
|
595
|
+
)
|
|
596
|
+
rc = rc and st.rc
|
|
597
|
+
dict_args = dict()
|
|
598
|
+
if rc:
|
|
599
|
+
rc, dict_args = self.sh.ecfsput(
|
|
600
|
+
source=psource, target=target, options=options
|
|
601
|
+
)
|
|
602
|
+
finally:
|
|
603
|
+
self.sh.rm(psource)
|
|
604
|
+
return rc, dict_args
|
|
605
|
+
else:
|
|
606
|
+
return self.sh.ecfsput(
|
|
607
|
+
source=source,
|
|
608
|
+
target=target,
|
|
609
|
+
options=options,
|
|
610
|
+
cpipeline=cpipeline,
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
fa_ecfsput = lfi_ecfsput = _std_ecfsput
|
|
614
|
+
|
|
615
|
+
@addons.require_external_addon("ectrans")
|
|
616
|
+
def _std_ectransput(
|
|
617
|
+
self,
|
|
618
|
+
source,
|
|
619
|
+
target,
|
|
620
|
+
gateway=None,
|
|
621
|
+
remote=None,
|
|
622
|
+
cpipeline=None,
|
|
623
|
+
sync=False,
|
|
624
|
+
):
|
|
625
|
+
"""
|
|
626
|
+
:param source: source file
|
|
627
|
+
:param target: target file
|
|
628
|
+
:param gateway: gateway used by ECtrans
|
|
629
|
+
:param remote: remote used by ECtrans
|
|
630
|
+
:param cpipeline: compression pipeline to be used, if provided
|
|
631
|
+
:param bool sync: If False, allow asynchronous transfers
|
|
632
|
+
:return: return code and additional attributes used
|
|
633
|
+
"""
|
|
634
|
+
if self.is_xlfi(source):
|
|
635
|
+
if cpipeline is not None:
|
|
636
|
+
raise OSError("It's not allowed to compress xlfi files.")
|
|
637
|
+
psource = self.sh.safe_fileaddsuffix(source)
|
|
638
|
+
rc = LFI_Status()
|
|
639
|
+
try:
|
|
640
|
+
st = self._std_copy(
|
|
641
|
+
source=source, destination=psource, pack=True
|
|
642
|
+
)
|
|
643
|
+
rc = rc and st.rc
|
|
644
|
+
dict_args = dict()
|
|
645
|
+
if rc:
|
|
646
|
+
rc, dict_args = self.sh.raw_ectransput(
|
|
647
|
+
source=psource,
|
|
648
|
+
target=target,
|
|
649
|
+
gateway=gateway,
|
|
650
|
+
remote=remote,
|
|
651
|
+
sync=sync,
|
|
652
|
+
)
|
|
653
|
+
finally:
|
|
654
|
+
self.sh.rm(psource)
|
|
655
|
+
return rc, dict_args
|
|
656
|
+
else:
|
|
657
|
+
return self.sh.ectransput(
|
|
658
|
+
source=source,
|
|
659
|
+
target=target,
|
|
660
|
+
gateway=gateway,
|
|
661
|
+
remote=remote,
|
|
662
|
+
cpipeline=cpipeline,
|
|
663
|
+
sync=sync,
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
fa_ectransput = lfi_ectransput = _std_ectransput
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
class LFI_Tool_Py(LFI_Tool_Raw):
|
|
670
|
+
"""
|
|
671
|
+
Rewritten Python interface to LFITOOLS command.
|
|
672
|
+
These commands are the one defined by the ``lfitools`` binary found in the IFS-ARPEGE framework.
|
|
673
|
+
WARNING: This interface is broken from cy41 onward.
|
|
674
|
+
"""
|
|
675
|
+
|
|
676
|
+
_footprint = dict(
|
|
677
|
+
info="Default LFI system interface (Python)",
|
|
678
|
+
attr=dict(
|
|
679
|
+
wraplanguage=dict(
|
|
680
|
+
values=[
|
|
681
|
+
"python",
|
|
682
|
+
],
|
|
683
|
+
),
|
|
684
|
+
),
|
|
685
|
+
)
|
|
686
|
+
|
|
687
|
+
def _pack_stream(self, source):
|
|
688
|
+
return self._spawn(
|
|
689
|
+
["lfi_alt_pack", "--lfi-file-in", source, "--lfi-file-out", "-"],
|
|
690
|
+
output=False,
|
|
691
|
+
inpipe=True,
|
|
692
|
+
bufsize=8192,
|
|
693
|
+
)
|
|
694
|
+
|
|
695
|
+
def _std_remove(self, *args):
|
|
696
|
+
"""Remove (possibly) multi lfi files."""
|
|
697
|
+
st = LFI_Status(result=list())
|
|
698
|
+
for pname in args:
|
|
699
|
+
for objpath in self.sh.glob(pname):
|
|
700
|
+
xlfi = self.is_xlfi(objpath)
|
|
701
|
+
if xlfi:
|
|
702
|
+
rc = self._spawn(
|
|
703
|
+
["lfi_alt_remv", "--lfi-file", objpath], output=False
|
|
704
|
+
)
|
|
705
|
+
else:
|
|
706
|
+
rc = self.sh.remove(objpath)
|
|
707
|
+
st.result.append(dict(path=objpath, multi=xlfi, rc=rc))
|
|
708
|
+
st.rc = rc
|
|
709
|
+
for dirpath in self.sh.glob(pname + ".d"):
|
|
710
|
+
if self.sh.path.exists(dirpath):
|
|
711
|
+
rc = self.sh.remove(dirpath)
|
|
712
|
+
st.result.append(dict(path=dirpath, multi=True, rc=rc))
|
|
713
|
+
st.rc = rc
|
|
714
|
+
return st
|
|
715
|
+
|
|
716
|
+
lfi_rm = lfi_remove = fa_rm = fa_remove = _std_remove
|
|
717
|
+
|
|
718
|
+
def _cp_pack_read(self, source, destination):
|
|
719
|
+
if self.warnpack:
|
|
720
|
+
logger.warning("Suspicious packing <%s>", source)
|
|
721
|
+
rc = self._spawn(
|
|
722
|
+
[
|
|
723
|
+
"lfi_alt_pack",
|
|
724
|
+
"--lfi-file-in",
|
|
725
|
+
source,
|
|
726
|
+
"--lfi-file-out",
|
|
727
|
+
destination,
|
|
728
|
+
],
|
|
729
|
+
output=False,
|
|
730
|
+
)
|
|
731
|
+
self.sh.chmod(destination, 0o444)
|
|
732
|
+
return rc
|
|
733
|
+
|
|
734
|
+
def _cp_pack_write(self, source, destination):
|
|
735
|
+
if self.warnpack:
|
|
736
|
+
logger.warning("Suspicious packing <%s>", source)
|
|
737
|
+
rc = self._spawn(
|
|
738
|
+
[
|
|
739
|
+
"lfi_alt_pack",
|
|
740
|
+
"--lfi-file-in",
|
|
741
|
+
source,
|
|
742
|
+
"--lfi-file-out",
|
|
743
|
+
destination,
|
|
744
|
+
],
|
|
745
|
+
output=False,
|
|
746
|
+
)
|
|
747
|
+
self.sh.chmod(destination, 0o644)
|
|
748
|
+
return rc
|
|
749
|
+
|
|
750
|
+
def _cp_copy_read(self, source, destination):
|
|
751
|
+
rc = self._spawn(
|
|
752
|
+
[
|
|
753
|
+
"lfi_alt_copy",
|
|
754
|
+
"--lfi-file-in",
|
|
755
|
+
source,
|
|
756
|
+
"--lfi-file-out",
|
|
757
|
+
destination,
|
|
758
|
+
],
|
|
759
|
+
output=False,
|
|
760
|
+
)
|
|
761
|
+
self.sh.chmod(destination, 0o444)
|
|
762
|
+
return rc
|
|
763
|
+
|
|
764
|
+
def _cp_copy_write(self, source, destination):
|
|
765
|
+
rc = self._spawn(
|
|
766
|
+
[
|
|
767
|
+
"lfi_alt_copy",
|
|
768
|
+
"--lfi-file-in",
|
|
769
|
+
source,
|
|
770
|
+
"--lfi-file-out",
|
|
771
|
+
destination,
|
|
772
|
+
],
|
|
773
|
+
output=False,
|
|
774
|
+
)
|
|
775
|
+
self.sh.chmod(destination, 0o644)
|
|
776
|
+
return rc
|
|
777
|
+
|
|
778
|
+
_cp_aspack_fsok_read = _cp_pack_read
|
|
779
|
+
_cp_aspack_fsok_write = _cp_pack_write
|
|
780
|
+
_cp_aspack_fsko_read = _cp_pack_read
|
|
781
|
+
_cp_aspack_fsko_write = _cp_pack_write
|
|
782
|
+
|
|
783
|
+
_cp_nopack_fsok_read = _cp_copy_read
|
|
784
|
+
_cp_nopack_fsok_write = _cp_copy_write
|
|
785
|
+
_cp_nopack_fsko_read = _cp_pack_read
|
|
786
|
+
_cp_nopack_fsko_write = _cp_pack_write
|
|
787
|
+
|
|
788
|
+
def _multicpmethod(self, pack=False, intent="in", samefs=False):
|
|
789
|
+
return "_cp_{:s}_{:s}_{:s}".format(
|
|
790
|
+
"aspack" if pack else "nopack",
|
|
791
|
+
"fsok" if samefs else "fsko",
|
|
792
|
+
"read" if intent == "in" else "write",
|
|
793
|
+
)
|
|
794
|
+
|
|
795
|
+
def _std_copy(
|
|
796
|
+
self,
|
|
797
|
+
source,
|
|
798
|
+
destination,
|
|
799
|
+
smartcp_threshold=0,
|
|
800
|
+
intent="in",
|
|
801
|
+
pack=False,
|
|
802
|
+
silent=False,
|
|
803
|
+
):
|
|
804
|
+
"""Extended copy for (possibly) multi lfi file."""
|
|
805
|
+
st = LFI_Status()
|
|
806
|
+
if not self.sh.path.exists(source):
|
|
807
|
+
logger.error("Missing source %s", source)
|
|
808
|
+
st.rc = 2
|
|
809
|
+
st.stderr = "No such source file or directory : [" + source + "]"
|
|
810
|
+
return st
|
|
811
|
+
if self.is_xlfi(source):
|
|
812
|
+
if not self.sh.filecocoon(destination):
|
|
813
|
+
raise OSError("Could not cocoon [" + destination + "]")
|
|
814
|
+
if not self.lfi_rm(destination):
|
|
815
|
+
raise OSError(
|
|
816
|
+
"Could not clean destination [" + destination + "]"
|
|
817
|
+
)
|
|
818
|
+
xcp = self._multicpmethod(
|
|
819
|
+
pack=pack,
|
|
820
|
+
intent=intent,
|
|
821
|
+
samefs=self.sh.is_samefs(source, destination),
|
|
822
|
+
)
|
|
823
|
+
actualcp = getattr(self, xcp, None)
|
|
824
|
+
if actualcp is None:
|
|
825
|
+
raise AttributeError("No actual LFI cp command " + xcp)
|
|
826
|
+
else:
|
|
827
|
+
st.rc = actualcp(source, self.sh.path.realpath(destination))
|
|
828
|
+
else:
|
|
829
|
+
if intent == "in":
|
|
830
|
+
st.rc = self.sh.smartcp(
|
|
831
|
+
source, destination, smartcp_threshold=smartcp_threshold
|
|
832
|
+
)
|
|
833
|
+
else:
|
|
834
|
+
st.rc = self.sh.cp(
|
|
835
|
+
source, destination, smartcp_threshold=smartcp_threshold
|
|
836
|
+
)
|
|
837
|
+
return st
|
|
838
|
+
|
|
839
|
+
lfi_cp = lfi_copy = fa_cp = fa_copy = _std_copy
|
|
840
|
+
|
|
841
|
+
def _std_move(self, source, destination, intent=None, pack=False):
|
|
842
|
+
"""Extended mv for (possibly) multi lfi file."""
|
|
843
|
+
if self.is_xlfi(source):
|
|
844
|
+
if intent is None:
|
|
845
|
+
intent = (
|
|
846
|
+
"inout" if self.sh.access(source, self.sh.W_OK) else "in"
|
|
847
|
+
)
|
|
848
|
+
st = self.lfi_cp(source, destination, intent=intent, pack=pack)
|
|
849
|
+
if st:
|
|
850
|
+
st = self.lfi_rm(source)
|
|
851
|
+
else:
|
|
852
|
+
st = LFI_Status()
|
|
853
|
+
st.rc = self.sh.mv(source, destination)
|
|
854
|
+
return st
|
|
855
|
+
|
|
856
|
+
lfi_mv = lfi_move = fa_mv = fa_move = _std_move
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
class IO_Poll(addons.Addon):
|
|
860
|
+
"""
|
|
861
|
+
Default interface to ``io_poll`` utility.
|
|
862
|
+
This addon is in charge of multi-file reshaping after IFS-ARPEGE execution.
|
|
863
|
+
"""
|
|
864
|
+
|
|
865
|
+
LFI_HNDL_SPEC = ":1"
|
|
866
|
+
DR_HOOK_SILENT = 1
|
|
867
|
+
DR_HOOK_NOT_MPI = 1
|
|
868
|
+
DR_HOOK_ASSERT_MPI_INITIALIZED = 0
|
|
869
|
+
OMP_STACKSIZE = "32M"
|
|
870
|
+
KMP_STACKSIZE = "32M"
|
|
871
|
+
KMP_MONITOR_STACKSIZE = "32M"
|
|
872
|
+
|
|
873
|
+
_footprint = dict(
|
|
874
|
+
info="Default io_poll system interface",
|
|
875
|
+
attr=dict(
|
|
876
|
+
kind=dict(
|
|
877
|
+
values=["iopoll", "io_poll"],
|
|
878
|
+
remap=dict(
|
|
879
|
+
io_poll="iopoll",
|
|
880
|
+
),
|
|
881
|
+
),
|
|
882
|
+
cfginfo=dict(
|
|
883
|
+
default="lfi",
|
|
884
|
+
),
|
|
885
|
+
cmd=dict(
|
|
886
|
+
alias=("iopollcmd", "io_pollcmd", "io_poll_cmd"),
|
|
887
|
+
default="io_poll",
|
|
888
|
+
),
|
|
889
|
+
path=dict(
|
|
890
|
+
alias=("iopollpath", "io_pollpath", "io_poll_path", "iopath"),
|
|
891
|
+
),
|
|
892
|
+
interpreter=dict(
|
|
893
|
+
values=["perl", "none", "None"],
|
|
894
|
+
remap=dict(
|
|
895
|
+
{
|
|
896
|
+
"None": "none",
|
|
897
|
+
}
|
|
898
|
+
),
|
|
899
|
+
default="perl",
|
|
900
|
+
optional=True,
|
|
901
|
+
),
|
|
902
|
+
toolkind=dict(default="iopoll"),
|
|
903
|
+
),
|
|
904
|
+
)
|
|
905
|
+
|
|
906
|
+
def __init__(self, *args, **kw):
|
|
907
|
+
"""Abstract Addon initialisation."""
|
|
908
|
+
logger.debug("IO_Poll init %s", self.__class__)
|
|
909
|
+
super().__init__(*args, **kw)
|
|
910
|
+
self._polled = set()
|
|
911
|
+
|
|
912
|
+
def _spawn(self, cmd, **kw):
|
|
913
|
+
"""Tube to set LFITOOLS env variable."""
|
|
914
|
+
if "LFITOOLS" not in self.env:
|
|
915
|
+
active_lfi = LFI_Tool_Raw.in_shell(
|
|
916
|
+
self.sh
|
|
917
|
+
) or LFI_Tool_Py.in_shell(self.sh)
|
|
918
|
+
if active_lfi is None:
|
|
919
|
+
raise RuntimeError("Could not find any active LFI Tool")
|
|
920
|
+
self.env.LFITOOLS = (
|
|
921
|
+
active_lfi.actual_path + "/" + active_lfi.actual_cmd
|
|
922
|
+
)
|
|
923
|
+
# Is there a need for an interpreter ?
|
|
924
|
+
if self.interpreter != "none":
|
|
925
|
+
kw["interpreter"] = self.interpreter
|
|
926
|
+
return super()._spawn(cmd, **kw)
|
|
927
|
+
|
|
928
|
+
def io_poll(self, prefix, nproc_io=None):
|
|
929
|
+
"""Do the actual job of polling files prefixed by ``prefix``."""
|
|
930
|
+
cmd = ["--prefix", prefix]
|
|
931
|
+
if nproc_io is None:
|
|
932
|
+
if not self.sh.path.exists("fort.4"):
|
|
933
|
+
raise OSError(
|
|
934
|
+
"The proc_io option or a fort.4 file should be provided."
|
|
935
|
+
)
|
|
936
|
+
else:
|
|
937
|
+
cmd.extend(["--nproc_io", str(nproc_io)])
|
|
938
|
+
|
|
939
|
+
# Catch the file processed
|
|
940
|
+
rawout = self._spawn(cmd)
|
|
941
|
+
|
|
942
|
+
# Cumulative results
|
|
943
|
+
st = LFI_Status()
|
|
944
|
+
st.result = rawout
|
|
945
|
+
for polledfile in st.result:
|
|
946
|
+
self._polled.add(polledfile)
|
|
947
|
+
st.rc &= self.sh.rclast
|
|
948
|
+
return st
|
|
949
|
+
|
|
950
|
+
@property
|
|
951
|
+
def polled(self):
|
|
952
|
+
"""List of files already polled."""
|
|
953
|
+
return sorted(self._polled)
|