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/targets.py
ADDED
|
@@ -0,0 +1,440 @@
|
|
|
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 = [
|
|
143
|
+
x
|
|
144
|
+
for x in (section + my_glove_rk, section)
|
|
145
|
+
if x in self.config.sections()
|
|
146
|
+
]
|
|
147
|
+
else:
|
|
148
|
+
option = key
|
|
149
|
+
# First look in override sections, then in default one
|
|
150
|
+
sections = [
|
|
151
|
+
s for s in self.config.sections() if s.endswith(my_glove_rk)
|
|
152
|
+
] + [
|
|
153
|
+
s
|
|
154
|
+
for s in self.config.sections()
|
|
155
|
+
if not self._re_glove_rk_id.match(s)
|
|
156
|
+
]
|
|
157
|
+
# Return the first matching section/option
|
|
158
|
+
for section in [
|
|
159
|
+
x for x in sections if self.config.has_option(x, option)
|
|
160
|
+
]:
|
|
161
|
+
return self.config.get(section, option)
|
|
162
|
+
return default
|
|
163
|
+
|
|
164
|
+
def getx(
|
|
165
|
+
self, key, default=None, env_key=None, silent=False, aslist=False
|
|
166
|
+
):
|
|
167
|
+
r"""Return a value from several sources.
|
|
168
|
+
|
|
169
|
+
In turn, the following sources are considered:
|
|
170
|
+
|
|
171
|
+
- a shell environment variable
|
|
172
|
+
- this configuration handler (key = 'section:option') (see the :meth:`get` method)
|
|
173
|
+
- a default value
|
|
174
|
+
|
|
175
|
+
Unless **silent** is set, ``KeyError`` is raised if the value cannot be found.
|
|
176
|
+
|
|
177
|
+
**aslist** forces the result into a list (be it with a unique element).
|
|
178
|
+
separators are spaces, commas, carriage returns or antislashes.
|
|
179
|
+
e.g. these notations are equivalent::
|
|
180
|
+
|
|
181
|
+
alist = val1 val2 val3 val4 val5
|
|
182
|
+
alist = val1, val2 val3 \
|
|
183
|
+
val4,
|
|
184
|
+
val5
|
|
185
|
+
|
|
186
|
+
"""
|
|
187
|
+
if env_key is not None:
|
|
188
|
+
env_key = env_key.upper()
|
|
189
|
+
value = sessions.system().env.get(env_key, None)
|
|
190
|
+
else:
|
|
191
|
+
value = None
|
|
192
|
+
|
|
193
|
+
if value is None:
|
|
194
|
+
if ":" not in key:
|
|
195
|
+
if silent:
|
|
196
|
+
return None
|
|
197
|
+
msg = 'Configuration key should be "section:option" not "{}"'.format(
|
|
198
|
+
key
|
|
199
|
+
)
|
|
200
|
+
raise KeyError(msg)
|
|
201
|
+
value = self.get(key, default)
|
|
202
|
+
|
|
203
|
+
if value is None:
|
|
204
|
+
if silent:
|
|
205
|
+
return None
|
|
206
|
+
msg = 'Please define "{}" in "{}"'.format(key, self.config.file)
|
|
207
|
+
if env_key is not None:
|
|
208
|
+
msg += ' or "{}" in the environment.'.format(env_key)
|
|
209
|
+
logger.error(msg)
|
|
210
|
+
raise KeyError(msg)
|
|
211
|
+
|
|
212
|
+
if aslist:
|
|
213
|
+
value = (
|
|
214
|
+
value.replace("\n", " ")
|
|
215
|
+
.replace("\\", " ")
|
|
216
|
+
.replace(",", " ")
|
|
217
|
+
.split()
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
return value
|
|
221
|
+
|
|
222
|
+
def sections(self):
|
|
223
|
+
"""Returns the list of sections contained in the config file."""
|
|
224
|
+
my_glove_rk = "@" + sessions.current().glove.realkind
|
|
225
|
+
return sorted(
|
|
226
|
+
{
|
|
227
|
+
self._re_glove_rk_id.sub(r"\1", x)
|
|
228
|
+
for x in self.config.sections()
|
|
229
|
+
if (
|
|
230
|
+
(not self._re_glove_rk_id.match(x))
|
|
231
|
+
or x.endswith(my_glove_rk)
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
def options(self, key):
|
|
237
|
+
"""For a given section, returns the list of available options.
|
|
238
|
+
|
|
239
|
+
The result may depend on the current glove (see the :meth:`get`
|
|
240
|
+
method documentation).
|
|
241
|
+
"""
|
|
242
|
+
my_glove_rk = "@" + sessions.current().glove.realkind
|
|
243
|
+
sections = [
|
|
244
|
+
x for x in (key, key + my_glove_rk) if x in self.config.sections()
|
|
245
|
+
]
|
|
246
|
+
options = set()
|
|
247
|
+
for section in sections:
|
|
248
|
+
options.update(self.config.options(section))
|
|
249
|
+
return sorted(options)
|
|
250
|
+
|
|
251
|
+
def items(self, key):
|
|
252
|
+
"""For a given section, returns a dict that contains all options.
|
|
253
|
+
|
|
254
|
+
The result may depend on the current glove (see the :meth:`get`
|
|
255
|
+
method documentation).
|
|
256
|
+
"""
|
|
257
|
+
items = dict()
|
|
258
|
+
if key is not None:
|
|
259
|
+
my_glove_rk = "@" + sessions.current().glove.realkind
|
|
260
|
+
sections = [
|
|
261
|
+
x
|
|
262
|
+
for x in (key, key + my_glove_rk)
|
|
263
|
+
if x in self.config.sections()
|
|
264
|
+
]
|
|
265
|
+
for section in sections:
|
|
266
|
+
items.update(self.config.items(section))
|
|
267
|
+
return items
|
|
268
|
+
|
|
269
|
+
@classmethod
|
|
270
|
+
def is_anonymous(cls):
|
|
271
|
+
"""Return a boolean either the current footprint define or not a mandatory set of hostname values."""
|
|
272
|
+
fp = cls.footprint_retrieve()
|
|
273
|
+
return not bool(fp.attr["hostname"]["values"])
|
|
274
|
+
|
|
275
|
+
def spawn_hook(self, sh):
|
|
276
|
+
"""Specific target hook before any serious execution."""
|
|
277
|
+
pass
|
|
278
|
+
|
|
279
|
+
@contextlib.contextmanager
|
|
280
|
+
def algo_run_context(self, ticket, *kmappings):
|
|
281
|
+
"""Specific target hook before any component run."""
|
|
282
|
+
yield
|
|
283
|
+
|
|
284
|
+
def _init_supernodes(
|
|
285
|
+
self,
|
|
286
|
+
main_re,
|
|
287
|
+
rangeid="range",
|
|
288
|
+
baseid="base",
|
|
289
|
+
):
|
|
290
|
+
"""Read the configuration file in order to initialize the specialnodes
|
|
291
|
+
and specialproxies lists.
|
|
292
|
+
|
|
293
|
+
To define a node list, the XXXnodes configuration key must be
|
|
294
|
+
specified. It can be an hardcoded coma-separated list, or the
|
|
295
|
+
*generic_nodes* keyword. In such a case, the node list will be
|
|
296
|
+
auto-generated using the XXXrange and XXXbase configuration keys.
|
|
297
|
+
"""
|
|
298
|
+
confsection = "generic_nodes"
|
|
299
|
+
confoptions = self.options(confsection)
|
|
300
|
+
nodetypes = [
|
|
301
|
+
(m.group(1), m.group(2))
|
|
302
|
+
for m in [main_re.match(k) for k in confoptions]
|
|
303
|
+
if m is not None
|
|
304
|
+
]
|
|
305
|
+
outdict = dict()
|
|
306
|
+
for nodetype, nodelistid in nodetypes:
|
|
307
|
+
nodelist = self.get(confsection + ":" + nodetype + nodelistid)
|
|
308
|
+
if nodelist == "no_generic":
|
|
309
|
+
noderanges = self.get(
|
|
310
|
+
confsection + ":" + nodetype + rangeid, None
|
|
311
|
+
)
|
|
312
|
+
if noderanges is None:
|
|
313
|
+
raise ValueError(
|
|
314
|
+
"when {0:s}{1:s} == no_generic, {0:s}{2:s} must be provided".format(
|
|
315
|
+
nodetype, nodelistid, rangeid
|
|
316
|
+
)
|
|
317
|
+
)
|
|
318
|
+
nodebases = self.get(
|
|
319
|
+
confsection + ":" + nodetype + baseid,
|
|
320
|
+
self.inetname + nodetype + "{:d}",
|
|
321
|
+
)
|
|
322
|
+
outdict[nodetype] = list()
|
|
323
|
+
for r, b in zip(noderanges.split("+"), nodebases.split("+")):
|
|
324
|
+
outdict[nodetype].extend(
|
|
325
|
+
[b.format(int(i)) for i in r.split(",")]
|
|
326
|
+
)
|
|
327
|
+
else:
|
|
328
|
+
outdict[nodetype] = nodelist.split(",")
|
|
329
|
+
return outdict
|
|
330
|
+
|
|
331
|
+
@property
|
|
332
|
+
def specialnodesaliases(self):
|
|
333
|
+
"""Return the list of known aliases."""
|
|
334
|
+
if self._sepcialnodesaliases is None:
|
|
335
|
+
confsection = "generic_nodes"
|
|
336
|
+
confoptions = self.options(confsection)
|
|
337
|
+
aliases_re = re.compile(r"(\w+)(aliases)")
|
|
338
|
+
nodetypes = [
|
|
339
|
+
(m.group(1), m.group(2))
|
|
340
|
+
for m in [aliases_re.match(k) for k in confoptions]
|
|
341
|
+
if m is not None
|
|
342
|
+
]
|
|
343
|
+
rdict = {
|
|
344
|
+
ntype: self.get(confsection + ":" + ntype + key, "").split(",")
|
|
345
|
+
for ntype, key in nodetypes
|
|
346
|
+
}
|
|
347
|
+
self._sepcialnodesaliases = rdict
|
|
348
|
+
return self._sepcialnodesaliases
|
|
349
|
+
|
|
350
|
+
@property
|
|
351
|
+
def specialnodes(self):
|
|
352
|
+
"""
|
|
353
|
+
Returns a dictionary that contains the list of nodes for a given
|
|
354
|
+
node-type.
|
|
355
|
+
"""
|
|
356
|
+
if self._specialnodes is None:
|
|
357
|
+
self._specialnodes = self._init_supernodes(self._re_nodes_property)
|
|
358
|
+
for ntype, aliases in self.specialnodesaliases.items():
|
|
359
|
+
for alias in aliases:
|
|
360
|
+
self._specialnodes[alias] = self._specialnodes[ntype]
|
|
361
|
+
return self._specialnodes
|
|
362
|
+
|
|
363
|
+
@property
|
|
364
|
+
def specialproxies(self):
|
|
365
|
+
"""Returns a dictionary that contains the proxy-nodes list for a given
|
|
366
|
+
node-type.
|
|
367
|
+
|
|
368
|
+
If the proxy-nodes are not defined in the configuration file, it is
|
|
369
|
+
equal to the specialnodes list.
|
|
370
|
+
"""
|
|
371
|
+
if self._specialproxies is None:
|
|
372
|
+
self._specialproxies = self._init_supernodes(
|
|
373
|
+
self._re_proxies_property, "proxiesrange", "proxiesbase"
|
|
374
|
+
)
|
|
375
|
+
for nodetype, nodelist in self.specialnodes.items():
|
|
376
|
+
if nodetype not in self._specialproxies:
|
|
377
|
+
self._specialproxies[nodetype] = nodelist
|
|
378
|
+
for ntype, aliases in self.specialnodesaliases.items():
|
|
379
|
+
for alias in aliases:
|
|
380
|
+
self._specialproxies[alias] = self._specialproxies[ntype]
|
|
381
|
+
return self._specialproxies
|
|
382
|
+
|
|
383
|
+
@secure_getattr
|
|
384
|
+
def __getattr__(self, key):
|
|
385
|
+
"""Create attributes on the fly.
|
|
386
|
+
|
|
387
|
+
* XXXnodes: returns the list of nodes for a given node-type
|
|
388
|
+
(e.g loginnodes). If the XXX node-type is not defined in the
|
|
389
|
+
configuration file, it returns an empty list.
|
|
390
|
+
* XXXproxies: returns the list of proxy nodes for a given node-type
|
|
391
|
+
(e.g loginproxies). If the XXX node-type is not defined in the
|
|
392
|
+
configuration file, it returns an empty list.
|
|
393
|
+
* isXXXnode: Return True if the current host is of XXX node-type.
|
|
394
|
+
If the XXX node-type is not defined in the configuration file,
|
|
395
|
+
it returns True.
|
|
396
|
+
|
|
397
|
+
"""
|
|
398
|
+
kmatch = self._re_nodes_property.match(key)
|
|
399
|
+
if kmatch is not None:
|
|
400
|
+
return fp.stdtypes.FPList(
|
|
401
|
+
self.specialnodes.get(kmatch.group(1), [])
|
|
402
|
+
)
|
|
403
|
+
kmatch = self._re_proxies_property.match(key)
|
|
404
|
+
if kmatch is not None:
|
|
405
|
+
return fp.stdtypes.FPList(
|
|
406
|
+
self.specialproxies.get(kmatch.group(1), [])
|
|
407
|
+
)
|
|
408
|
+
kmatch = self._re_isnode_property.match(key)
|
|
409
|
+
if kmatch is not None:
|
|
410
|
+
return (kmatch.group(1) not in self.specialnodes) or any(
|
|
411
|
+
[
|
|
412
|
+
self.hostname.startswith(s)
|
|
413
|
+
for s in self.specialnodes[kmatch.group(1)]
|
|
414
|
+
]
|
|
415
|
+
)
|
|
416
|
+
raise AttributeError('The key "{:s}" does not exist.'.format(key))
|
|
417
|
+
|
|
418
|
+
@property
|
|
419
|
+
def ftraw_default(self):
|
|
420
|
+
"""The default value for the System object ftraw attribute."""
|
|
421
|
+
return "ftraw" in self.specialnodes and any(
|
|
422
|
+
[self.hostname.startswith(s) for s in self.specialnodes["ftraw"]]
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
class LocalTarget(Target):
|
|
427
|
+
"""A very generic class usable for most computers."""
|
|
428
|
+
|
|
429
|
+
_footprint = dict(
|
|
430
|
+
info="Nice local target",
|
|
431
|
+
attr=dict(
|
|
432
|
+
sysname=dict(values=["Linux", "Darwin", "Local", "Localhost"]),
|
|
433
|
+
),
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
# Disable priority warnings on the target collector
|
|
438
|
+
fcollect = fp.collectors.get(tag="target")
|
|
439
|
+
fcollect.non_ambiguous_loglevel = logging.DEBUG
|
|
440
|
+
del fcollect
|