vortex-nwp 2.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.
Files changed (146) hide show
  1. vortex/__init__.py +135 -0
  2. vortex/algo/__init__.py +12 -0
  3. vortex/algo/components.py +2136 -0
  4. vortex/algo/mpitools.py +1648 -0
  5. vortex/algo/mpitools_templates/envelope_wrapper_default.tpl +27 -0
  6. vortex/algo/mpitools_templates/envelope_wrapper_mpiauto.tpl +29 -0
  7. vortex/algo/mpitools_templates/wrapstd_wrapper_default.tpl +18 -0
  8. vortex/algo/serversynctools.py +170 -0
  9. vortex/config.py +115 -0
  10. vortex/data/__init__.py +13 -0
  11. vortex/data/abstractstores.py +1572 -0
  12. vortex/data/containers.py +780 -0
  13. vortex/data/contents.py +596 -0
  14. vortex/data/executables.py +284 -0
  15. vortex/data/flow.py +113 -0
  16. vortex/data/geometries.ini +2689 -0
  17. vortex/data/geometries.py +703 -0
  18. vortex/data/handlers.py +1021 -0
  19. vortex/data/outflow.py +67 -0
  20. vortex/data/providers.py +465 -0
  21. vortex/data/resources.py +201 -0
  22. vortex/data/stores.py +1271 -0
  23. vortex/gloves.py +282 -0
  24. vortex/layout/__init__.py +27 -0
  25. vortex/layout/appconf.py +109 -0
  26. vortex/layout/contexts.py +511 -0
  27. vortex/layout/dataflow.py +1069 -0
  28. vortex/layout/jobs.py +1276 -0
  29. vortex/layout/monitor.py +833 -0
  30. vortex/layout/nodes.py +1424 -0
  31. vortex/layout/subjobs.py +464 -0
  32. vortex/nwp/__init__.py +11 -0
  33. vortex/nwp/algo/__init__.py +12 -0
  34. vortex/nwp/algo/assim.py +483 -0
  35. vortex/nwp/algo/clim.py +920 -0
  36. vortex/nwp/algo/coupling.py +609 -0
  37. vortex/nwp/algo/eda.py +632 -0
  38. vortex/nwp/algo/eps.py +613 -0
  39. vortex/nwp/algo/forecasts.py +745 -0
  40. vortex/nwp/algo/fpserver.py +927 -0
  41. vortex/nwp/algo/ifsnaming.py +403 -0
  42. vortex/nwp/algo/ifsroot.py +311 -0
  43. vortex/nwp/algo/monitoring.py +202 -0
  44. vortex/nwp/algo/mpitools.py +554 -0
  45. vortex/nwp/algo/odbtools.py +974 -0
  46. vortex/nwp/algo/oopsroot.py +735 -0
  47. vortex/nwp/algo/oopstests.py +186 -0
  48. vortex/nwp/algo/request.py +579 -0
  49. vortex/nwp/algo/stdpost.py +1285 -0
  50. vortex/nwp/data/__init__.py +12 -0
  51. vortex/nwp/data/assim.py +392 -0
  52. vortex/nwp/data/boundaries.py +261 -0
  53. vortex/nwp/data/climfiles.py +539 -0
  54. vortex/nwp/data/configfiles.py +149 -0
  55. vortex/nwp/data/consts.py +929 -0
  56. vortex/nwp/data/ctpini.py +133 -0
  57. vortex/nwp/data/diagnostics.py +181 -0
  58. vortex/nwp/data/eda.py +148 -0
  59. vortex/nwp/data/eps.py +383 -0
  60. vortex/nwp/data/executables.py +1039 -0
  61. vortex/nwp/data/fields.py +96 -0
  62. vortex/nwp/data/gridfiles.py +308 -0
  63. vortex/nwp/data/logs.py +551 -0
  64. vortex/nwp/data/modelstates.py +334 -0
  65. vortex/nwp/data/monitoring.py +220 -0
  66. vortex/nwp/data/namelists.py +644 -0
  67. vortex/nwp/data/obs.py +748 -0
  68. vortex/nwp/data/oopsexec.py +72 -0
  69. vortex/nwp/data/providers.py +182 -0
  70. vortex/nwp/data/query.py +217 -0
  71. vortex/nwp/data/stores.py +147 -0
  72. vortex/nwp/data/surfex.py +338 -0
  73. vortex/nwp/syntax/__init__.py +9 -0
  74. vortex/nwp/syntax/stdattrs.py +375 -0
  75. vortex/nwp/tools/__init__.py +10 -0
  76. vortex/nwp/tools/addons.py +35 -0
  77. vortex/nwp/tools/agt.py +55 -0
  78. vortex/nwp/tools/bdap.py +48 -0
  79. vortex/nwp/tools/bdcp.py +38 -0
  80. vortex/nwp/tools/bdm.py +21 -0
  81. vortex/nwp/tools/bdmp.py +49 -0
  82. vortex/nwp/tools/conftools.py +1311 -0
  83. vortex/nwp/tools/drhook.py +62 -0
  84. vortex/nwp/tools/grib.py +268 -0
  85. vortex/nwp/tools/gribdiff.py +99 -0
  86. vortex/nwp/tools/ifstools.py +163 -0
  87. vortex/nwp/tools/igastuff.py +249 -0
  88. vortex/nwp/tools/mars.py +56 -0
  89. vortex/nwp/tools/odb.py +548 -0
  90. vortex/nwp/tools/partitioning.py +234 -0
  91. vortex/nwp/tools/satrad.py +56 -0
  92. vortex/nwp/util/__init__.py +6 -0
  93. vortex/nwp/util/async.py +184 -0
  94. vortex/nwp/util/beacon.py +40 -0
  95. vortex/nwp/util/diffpygram.py +359 -0
  96. vortex/nwp/util/ens.py +198 -0
  97. vortex/nwp/util/hooks.py +128 -0
  98. vortex/nwp/util/taskdeco.py +81 -0
  99. vortex/nwp/util/usepygram.py +591 -0
  100. vortex/nwp/util/usetnt.py +87 -0
  101. vortex/proxy.py +6 -0
  102. vortex/sessions.py +341 -0
  103. vortex/syntax/__init__.py +9 -0
  104. vortex/syntax/stdattrs.py +628 -0
  105. vortex/syntax/stddeco.py +176 -0
  106. vortex/toolbox.py +982 -0
  107. vortex/tools/__init__.py +11 -0
  108. vortex/tools/actions.py +457 -0
  109. vortex/tools/addons.py +297 -0
  110. vortex/tools/arm.py +76 -0
  111. vortex/tools/compression.py +322 -0
  112. vortex/tools/date.py +20 -0
  113. vortex/tools/ddhpack.py +10 -0
  114. vortex/tools/delayedactions.py +672 -0
  115. vortex/tools/env.py +513 -0
  116. vortex/tools/folder.py +663 -0
  117. vortex/tools/grib.py +559 -0
  118. vortex/tools/lfi.py +746 -0
  119. vortex/tools/listings.py +354 -0
  120. vortex/tools/names.py +575 -0
  121. vortex/tools/net.py +1790 -0
  122. vortex/tools/odb.py +10 -0
  123. vortex/tools/parallelism.py +336 -0
  124. vortex/tools/prestaging.py +186 -0
  125. vortex/tools/rawfiles.py +10 -0
  126. vortex/tools/schedulers.py +413 -0
  127. vortex/tools/services.py +871 -0
  128. vortex/tools/storage.py +1061 -0
  129. vortex/tools/surfex.py +61 -0
  130. vortex/tools/systems.py +3396 -0
  131. vortex/tools/targets.py +384 -0
  132. vortex/util/__init__.py +9 -0
  133. vortex/util/config.py +1071 -0
  134. vortex/util/empty.py +24 -0
  135. vortex/util/helpers.py +184 -0
  136. vortex/util/introspection.py +63 -0
  137. vortex/util/iosponge.py +76 -0
  138. vortex/util/roles.py +51 -0
  139. vortex/util/storefunctions.py +103 -0
  140. vortex/util/structs.py +26 -0
  141. vortex/util/worker.py +150 -0
  142. vortex_nwp-2.0.0b1.dist-info/LICENSE +517 -0
  143. vortex_nwp-2.0.0b1.dist-info/METADATA +50 -0
  144. vortex_nwp-2.0.0b1.dist-info/RECORD +146 -0
  145. vortex_nwp-2.0.0b1.dist-info/WHEEL +5 -0
  146. vortex_nwp-2.0.0b1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,384 @@
1
+ """
2
+ This package handles targets computers objects that could in charge of
3
+ hosting a specific execution. Target objects use the :mod:`footprints` mechanism.
4
+ """
5
+
6
+ import contextlib
7
+ import logging
8
+ import re
9
+ import platform
10
+ import socket
11
+
12
+ from bronx.fancies import loggers
13
+ from bronx.syntax.decorators import secure_getattr
14
+ import footprints as fp
15
+
16
+ from vortex.util.config import GenericConfigParser
17
+ from vortex import sessions
18
+
19
+ #: No automatic export
20
+ __all__ = []
21
+
22
+ logger = loggers.getLogger(__name__)
23
+
24
+
25
+ def default_fqdn():
26
+ """Tries to find the Fully-Qualified Domain Name of the host."""
27
+ try:
28
+ fqdn = socket.getfqdn()
29
+ except OSError:
30
+ fqdn = platform.node()
31
+ return fqdn
32
+
33
+
34
+ class Target(fp.FootprintBase):
35
+ """Root class for any :class:`Target` subclasses.
36
+
37
+ Target classes are used to define specific settings and/or behaviour for a
38
+ given host (*e.g.* your own workstation) or group of hosts (*e.g.* each of
39
+ the nodes of a cluster).
40
+
41
+ Through the :meth:`get` method, it gives access to the **Target**'s specific
42
+ configuration file (``target-[hostname].ini`` by default).
43
+ """
44
+
45
+ _abstract = True
46
+ _explicit = False
47
+ _collector = ('target',)
48
+ _footprint = dict(
49
+ info = 'Default target description',
50
+ attr = dict(
51
+ hostname = dict(
52
+ optional = True,
53
+ default = platform.node(),
54
+ alias = ('nodename', 'computer')
55
+ ),
56
+ inetname = dict(
57
+ optional = True,
58
+ default = platform.node(),
59
+ ),
60
+ fqdn = dict(
61
+ optional = True,
62
+ default = default_fqdn(),
63
+ ),
64
+ sysname = dict(
65
+ optional = True,
66
+ default = platform.system(),
67
+ ),
68
+ userconfig = dict(
69
+ type = GenericConfigParser,
70
+ optional = True,
71
+ default = None,
72
+ ),
73
+ inifile = dict(
74
+ optional = True,
75
+ default = '@target-[hostname].ini',
76
+ ),
77
+ defaultinifile = dict(
78
+ optional = True,
79
+ default = 'target-commons.ini',
80
+ ),
81
+ iniauto = dict(
82
+ type = bool,
83
+ optional = True,
84
+ default = True,
85
+ )
86
+ )
87
+ )
88
+
89
+ _re_nodes_property = re.compile(r'(\w+)(nodes)$')
90
+ _re_proxies_property = re.compile(r'(\w+)(proxies)$')
91
+ _re_isnode_property = re.compile(r'is(\w+)node$')
92
+ _re_glove_rk_id = re.compile(r'^(.*)@\w+$')
93
+
94
+ def __init__(self, *args, **kw):
95
+ logger.debug('Abstract target computer init %s', self.__class__)
96
+ super().__init__(*args, **kw)
97
+ self._actualconfig = self.userconfig
98
+ self._specialnodes = None
99
+ self._sepcialnodesaliases = None
100
+ self._specialproxies = None
101
+
102
+ @property
103
+ def realkind(self):
104
+ return 'target'
105
+
106
+ @property
107
+ def config(self):
108
+ return self._actualconfig
109
+
110
+ def generic(self):
111
+ """Generic name is inetname by default."""
112
+ return self.inetname
113
+
114
+ def cache_storage_alias(self):
115
+ """The tag used when reading Cache Storage configuration files."""
116
+ return self.inetname
117
+
118
+ def get(self, key, default=None):
119
+ """Get the actual value of the specified ``key`` ( ``section:option`` ).
120
+
121
+ Sections of the configuration file may be overwritten with sections
122
+ specific to a given user's group (identified by the Glove's realkind
123
+ property).
124
+
125
+ :example:
126
+
127
+ Let's consider a user with the *opuser* Glove's realkind and
128
+ the following configuration file::
129
+
130
+ [sectionname]
131
+ myoption = generic
132
+ [sectionname@opuser]
133
+ myoption = operations
134
+
135
+ The :meth:`get` method called whith ``key='sectionname:myoption'`` will
136
+ return 'operations'.
137
+ """
138
+ my_glove_rk = '@' + sessions.current().glove.realkind
139
+ if ':' in key:
140
+ section, option = (x.strip() for x in key.split(':', 1))
141
+ # Check if an override section exists
142
+ sections = [x for x in (section + my_glove_rk, section)
143
+ if x in self.config.sections()]
144
+ else:
145
+ option = key
146
+ # First look in override sections, then in default one
147
+ sections = ([s for s in self.config.sections() if s.endswith(my_glove_rk)] +
148
+ [s for s in self.config.sections() if not self._re_glove_rk_id.match(s)])
149
+ # Return the first matching section/option
150
+ for section in [x for x in sections if self.config.has_option(x, option)]:
151
+ return self.config.get(section, option)
152
+ return default
153
+
154
+ def getx(self, key, default=None, env_key=None, silent=False, aslist=False):
155
+ r"""Return a value from several sources.
156
+
157
+ In turn, the following sources are considered:
158
+
159
+ - a shell environment variable
160
+ - this configuration handler (key = 'section:option') (see the :meth:`get` method)
161
+ - a default value
162
+
163
+ Unless **silent** is set, ``KeyError`` is raised if the value cannot be found.
164
+
165
+ **aslist** forces the result into a list (be it with a unique element).
166
+ separators are spaces, commas, carriage returns or antislashes.
167
+ e.g. these notations are equivalent::
168
+
169
+ alist = val1 val2 val3 val4 val5
170
+ alist = val1, val2 val3 \
171
+ val4,
172
+ val5
173
+
174
+ """
175
+ if env_key is not None:
176
+ env_key = env_key.upper()
177
+ value = sessions.system().env.get(env_key, None)
178
+ else:
179
+ value = None
180
+
181
+ if value is None:
182
+ if ':' not in key:
183
+ if silent:
184
+ return None
185
+ msg = 'Configuration key should be "section:option" not "{}"'.format(key)
186
+ raise KeyError(msg)
187
+ value = self.get(key, default)
188
+
189
+ if value is None:
190
+ if silent:
191
+ return None
192
+ msg = 'Please define "{}" in "{}"'.format(key, self.config.file)
193
+ if env_key is not None:
194
+ msg += ' or "{}" in the environment.'.format(env_key)
195
+ logger.error(msg)
196
+ raise KeyError(msg)
197
+
198
+ if aslist:
199
+ value = value.replace('\n', ' ').replace('\\', ' ').replace(',', ' ').split()
200
+
201
+ return value
202
+
203
+ def sections(self):
204
+ """Returns the list of sections contained in the config file."""
205
+ my_glove_rk = '@' + sessions.current().glove.realkind
206
+ return sorted({self._re_glove_rk_id.sub(r'\1', x)
207
+ for x in self.config.sections()
208
+ if ((not self._re_glove_rk_id.match(x)) or
209
+ x.endswith(my_glove_rk))})
210
+
211
+ def options(self, key):
212
+ """For a given section, returns the list of available options.
213
+
214
+ The result may depend on the current glove (see the :meth:`get`
215
+ method documentation).
216
+ """
217
+ my_glove_rk = '@' + sessions.current().glove.realkind
218
+ sections = [x for x in (key, key + my_glove_rk)
219
+ if x in self.config.sections()]
220
+ options = set()
221
+ for section in sections:
222
+ options.update(self.config.options(section))
223
+ return sorted(options)
224
+
225
+ def items(self, key):
226
+ """For a given section, returns a dict that contains all options.
227
+
228
+ The result may depend on the current glove (see the :meth:`get`
229
+ method documentation).
230
+ """
231
+ items = dict()
232
+ if key is not None:
233
+ my_glove_rk = '@' + sessions.current().glove.realkind
234
+ sections = [x for x in (key, key + my_glove_rk)
235
+ if x in self.config.sections()]
236
+ for section in sections:
237
+ items.update(self.config.items(section))
238
+ return items
239
+
240
+ @classmethod
241
+ def is_anonymous(cls):
242
+ """Return a boolean either the current footprint define or not a mandatory set of hostname values."""
243
+ fp = cls.footprint_retrieve()
244
+ return not bool(fp.attr['hostname']['values'])
245
+
246
+ def spawn_hook(self, sh):
247
+ """Specific target hook before any serious execution."""
248
+ pass
249
+
250
+ @contextlib.contextmanager
251
+ def algo_run_context(self, ticket, *kmappings):
252
+ """Specific target hook before any component run."""
253
+ yield
254
+
255
+ def _init_supernodes(self, main_re, rangeid='range', baseid='base',):
256
+ """Read the configuration file in order to initialize the specialnodes
257
+ and specialproxies lists.
258
+
259
+ To define a node list, the XXXnodes configuration key must be
260
+ specified. It can be an hardcoded coma-separated list, or the
261
+ *generic_nodes* keyword. In such a case, the node list will be
262
+ auto-generated using the XXXrange and XXXbase configuration keys.
263
+ """
264
+ confsection = 'generic_nodes'
265
+ confoptions = self.options(confsection)
266
+ nodetypes = [(m.group(1), m.group(2))
267
+ for m in [main_re.match(k) for k in confoptions]
268
+ if m is not None]
269
+ outdict = dict()
270
+ for nodetype, nodelistid in nodetypes:
271
+ nodelist = self.get(confsection + ':' + nodetype + nodelistid)
272
+ if nodelist == 'no_generic':
273
+ noderanges = self.get(confsection + ':' + nodetype + rangeid, None)
274
+ if noderanges is None:
275
+ raise ValueError('when {0:s}{1:s} == no_generic, {0:s}{2:s} must be provided'
276
+ .format(nodetype, nodelistid, rangeid))
277
+ nodebases = self.get(confsection + ':' + nodetype + baseid,
278
+ self.inetname + nodetype + '{:d}')
279
+ outdict[nodetype] = list()
280
+ for (r, b) in zip(noderanges.split('+'), nodebases.split('+')):
281
+ outdict[nodetype].extend([b.format(int(i)) for i in r.split(',')])
282
+ else:
283
+ outdict[nodetype] = nodelist.split(',')
284
+ return outdict
285
+
286
+ @property
287
+ def specialnodesaliases(self):
288
+ """Return the list of known aliases."""
289
+ if self._sepcialnodesaliases is None:
290
+ confsection = 'generic_nodes'
291
+ confoptions = self.options(confsection)
292
+ aliases_re = re.compile(r'(\w+)(aliases)')
293
+ nodetypes = [(m.group(1), m.group(2))
294
+ for m in [aliases_re.match(k) for k in confoptions]
295
+ if m is not None]
296
+ rdict = {ntype: self.get(confsection + ':' + ntype + key, '').split(',')
297
+ for ntype, key in nodetypes}
298
+ self._sepcialnodesaliases = rdict
299
+ return self._sepcialnodesaliases
300
+
301
+ @property
302
+ def specialnodes(self):
303
+ """
304
+ Returns a dictionary that contains the list of nodes for a given
305
+ node-type.
306
+ """
307
+ if self._specialnodes is None:
308
+ self._specialnodes = self._init_supernodes(self._re_nodes_property)
309
+ for ntype, aliases in self.specialnodesaliases.items():
310
+ for alias in aliases:
311
+ self._specialnodes[alias] = self._specialnodes[ntype]
312
+ return self._specialnodes
313
+
314
+ @property
315
+ def specialproxies(self):
316
+ """Returns a dictionary that contains the proxy-nodes list for a given
317
+ node-type.
318
+
319
+ If the proxy-nodes are not defined in the configuration file, it is
320
+ equal to the specialnodes list.
321
+ """
322
+ if self._specialproxies is None:
323
+ self._specialproxies = self._init_supernodes(self._re_proxies_property, 'proxiesrange', 'proxiesbase')
324
+ for nodetype, nodelist in self.specialnodes.items():
325
+ if nodetype not in self._specialproxies:
326
+ self._specialproxies[nodetype] = nodelist
327
+ for ntype, aliases in self.specialnodesaliases.items():
328
+ for alias in aliases:
329
+ self._specialproxies[alias] = self._specialproxies[ntype]
330
+ return self._specialproxies
331
+
332
+ @secure_getattr
333
+ def __getattr__(self, key):
334
+ """Create attributes on the fly.
335
+
336
+ * XXXnodes: returns the list of nodes for a given node-type
337
+ (e.g loginnodes). If the XXX node-type is not defined in the
338
+ configuration file, it returns an empty list.
339
+ * XXXproxies: returns the list of proxy nodes for a given node-type
340
+ (e.g loginproxies). If the XXX node-type is not defined in the
341
+ configuration file, it returns an empty list.
342
+ * isXXXnode: Return True if the current host is of XXX node-type.
343
+ If the XXX node-type is not defined in the configuration file,
344
+ it returns True.
345
+
346
+ """
347
+ kmatch = self._re_nodes_property.match(key)
348
+ if kmatch is not None:
349
+ return fp.stdtypes.FPList(self.specialnodes.get(kmatch.group(1), []))
350
+ kmatch = self._re_proxies_property.match(key)
351
+ if kmatch is not None:
352
+ return fp.stdtypes.FPList(self.specialproxies.get(kmatch.group(1), []))
353
+ kmatch = self._re_isnode_property.match(key)
354
+ if kmatch is not None:
355
+ return ((kmatch.group(1) not in self.specialnodes) or
356
+ any([self.hostname.startswith(s)
357
+ for s in self.specialnodes[kmatch.group(1)]]))
358
+ raise AttributeError('The key "{:s}" does not exist.'.format(key))
359
+
360
+ @property
361
+ def ftraw_default(self):
362
+ """The default value for the System object ftraw attribute."""
363
+ return ('ftraw' in self.specialnodes and
364
+ any([self.hostname.startswith(s)
365
+ for s in self.specialnodes['ftraw']]))
366
+
367
+
368
+ class LocalTarget(Target):
369
+ """A very generic class usable for most computers."""
370
+
371
+ _footprint = dict(
372
+ info = 'Nice local target',
373
+ attr = dict(
374
+ sysname = dict(
375
+ values = ['Linux', 'Darwin', 'Local', 'Localhost']
376
+ ),
377
+ )
378
+ )
379
+
380
+
381
+ # Disable priority warnings on the target collector
382
+ fcollect = fp.collectors.get(tag='target')
383
+ fcollect.non_ambiguous_loglevel = logging.DEBUG
384
+ del fcollect
@@ -0,0 +1,9 @@
1
+ """
2
+ Miscaleneous collection of small tools somehow extending standard Python's
3
+ capabilities.
4
+ """
5
+
6
+ #: No automatic export
7
+ __all__ = []
8
+
9
+ __tocinfoline__ = 'VORTEX generic utility classes and methods'