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/__init__.py
CHANGED
|
@@ -3,9 +3,18 @@ This is a pure package containing several modules that could be used
|
|
|
3
3
|
as standalone tools.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
-
from . import storage
|
|
6
|
+
from . import storage as storage
|
|
7
|
+
from . import schedulers as schedulers
|
|
8
|
+
from . import services as services
|
|
9
|
+
from . import systems as systems
|
|
10
|
+
from . import targets as targets
|
|
11
|
+
from . import date as date
|
|
12
|
+
from . import env as env
|
|
13
|
+
from . import names as names
|
|
7
14
|
|
|
8
15
|
#: No automatic export
|
|
9
16
|
__all__ = []
|
|
10
17
|
|
|
11
|
-
__tocinfoline__ =
|
|
18
|
+
__tocinfoline__ = (
|
|
19
|
+
"VORTEX generic tools (system interfaces, format handling, ...)"
|
|
20
|
+
)
|
vortex/tools/actions.py
CHANGED
|
@@ -10,9 +10,6 @@ to be processed: e.g. mail, routing, alarm.
|
|
|
10
10
|
import bronx.stdtypes.catalog
|
|
11
11
|
import footprints
|
|
12
12
|
from bronx.fancies import loggers
|
|
13
|
-
from bronx.fancies.display import dict_as_str
|
|
14
|
-
from vortex import sessions
|
|
15
|
-
from vortex.util.config import GenericConfigParser
|
|
16
13
|
|
|
17
14
|
#: Export nothing
|
|
18
15
|
__all__ = []
|
|
@@ -27,9 +24,11 @@ class Action:
|
|
|
27
24
|
Such an action could be activated or not, and is basically driven by permissions settings.
|
|
28
25
|
"""
|
|
29
26
|
|
|
30
|
-
def __init__(
|
|
27
|
+
def __init__(
|
|
28
|
+
self, kind="foo", service=None, active=False, permanent=False
|
|
29
|
+
):
|
|
31
30
|
if service is None:
|
|
32
|
-
service =
|
|
31
|
+
service = "send" + kind
|
|
33
32
|
self._service = service
|
|
34
33
|
self._kind = kind
|
|
35
34
|
self._active = active
|
|
@@ -81,8 +80,8 @@ class Action:
|
|
|
81
80
|
|
|
82
81
|
def info(self):
|
|
83
82
|
"""Informative string (may serve debugging purposes)."""
|
|
84
|
-
return
|
|
85
|
-
|
|
83
|
+
return "{} Action {} (kind={})".format(
|
|
84
|
+
"ON " if self.status() else "OFF",
|
|
86
85
|
self.__class__.__name__,
|
|
87
86
|
self.kind,
|
|
88
87
|
)
|
|
@@ -94,7 +93,7 @@ class Action:
|
|
|
94
93
|
def service_info(self, **kw):
|
|
95
94
|
"""On the fly remapping of the expected footprint."""
|
|
96
95
|
info = dict(kw)
|
|
97
|
-
info.setdefault(
|
|
96
|
+
info.setdefault("kind", self.service_kind(**kw))
|
|
98
97
|
return info
|
|
99
98
|
|
|
100
99
|
def get_actual_service(self, **kw):
|
|
@@ -114,9 +113,11 @@ class Action:
|
|
|
114
113
|
if self.active:
|
|
115
114
|
a_service = self.get_actual_service(**kw)
|
|
116
115
|
if a_service is None:
|
|
117
|
-
logger.warning(
|
|
116
|
+
logger.warning(
|
|
117
|
+
"Could not find any service for action %s", self.kind
|
|
118
|
+
)
|
|
118
119
|
else:
|
|
119
|
-
logger.warning(
|
|
120
|
+
logger.warning("Action %s is not active", self.kind)
|
|
120
121
|
return a_service
|
|
121
122
|
|
|
122
123
|
def execute(self, *args, **kw):
|
|
@@ -131,94 +132,30 @@ class Action:
|
|
|
131
132
|
class TunableAction(Action):
|
|
132
133
|
"""An Action that may be tuned
|
|
133
134
|
|
|
134
|
-
|
|
135
|
-
- accepts the syntax `ad.action_tune(key=value)` (which has priority)
|
|
135
|
+
accepts the syntax `ad.action_tune(key=value)` (which has priority)
|
|
136
136
|
"""
|
|
137
137
|
|
|
138
138
|
def __init__(self, configuration=None, **kwargs):
|
|
139
139
|
super().__init__(**kwargs)
|
|
140
140
|
self._tuning = dict()
|
|
141
|
-
self._conf_section = configuration
|
|
142
|
-
self._conf_dict = None
|
|
143
|
-
|
|
144
|
-
@property
|
|
145
|
-
def _shtarget(self):
|
|
146
|
-
"""Warning: this may be a `vortex.syntax.stdattrs.DelayedInit` object
|
|
147
|
-
during Vortex initialization and may not have a `sections()` method
|
|
148
|
-
nor a `config` property.
|
|
149
|
-
"""
|
|
150
|
-
return sessions.current().sh.default_target
|
|
151
|
-
|
|
152
|
-
@property
|
|
153
|
-
def _conf_items(self):
|
|
154
|
-
"""Check and return the configuration: a section in the target-xxx.ini file.
|
|
155
|
-
|
|
156
|
-
If the configuration is None, an attempt is made to use the Action's kind.
|
|
157
|
-
Don't use before Vortex initialization is done (see `_shtarget`).
|
|
158
|
-
"""
|
|
159
|
-
if self._conf_dict is None:
|
|
160
|
-
if self._conf_section is None:
|
|
161
|
-
if self.kind in self._shtarget.sections():
|
|
162
|
-
self._conf_section = self.kind
|
|
163
|
-
else:
|
|
164
|
-
if self._conf_section not in self._shtarget.sections():
|
|
165
|
-
raise KeyError('No section "{}" in "{}"'.format(self._conf_section,
|
|
166
|
-
self._shtarget.config.file))
|
|
167
|
-
if self._conf_section is None:
|
|
168
|
-
self._conf_dict = dict()
|
|
169
|
-
else:
|
|
170
|
-
self._conf_dict = self._shtarget.items(self._conf_section)
|
|
171
|
-
return self._conf_dict
|
|
172
141
|
|
|
173
142
|
def service_info(self, **kw):
|
|
174
|
-
for k, v in self.
|
|
143
|
+
for k, v in self._tuning.items():
|
|
175
144
|
kw.setdefault(k, v)
|
|
176
145
|
return super().service_info(**kw)
|
|
177
146
|
|
|
178
|
-
def tune(self,
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
``section`` is a specific section name, or ``None`` for all.
|
|
182
|
-
"""
|
|
183
|
-
if section is None or section == self._conf_section:
|
|
184
|
-
self._tuning.update(kw)
|
|
185
|
-
|
|
186
|
-
def _get_config_dict(self):
|
|
187
|
-
final_dict = dict()
|
|
188
|
-
final_dict.update(self._conf_items)
|
|
189
|
-
final_dict.update(self._tuning)
|
|
190
|
-
return final_dict
|
|
191
|
-
|
|
192
|
-
def info(self):
|
|
193
|
-
"""Informative string (may serve debugging purposes)."""
|
|
194
|
-
s = super().info() + ' - tunable\n'
|
|
195
|
-
mix = dict()
|
|
196
|
-
mix.update(self._conf_items)
|
|
197
|
-
mix.update(self._tuning)
|
|
198
|
-
prt = dict()
|
|
199
|
-
for k, v in mix.items():
|
|
200
|
-
if k in self._tuning:
|
|
201
|
-
prt['++ ' + k] = '{} (was: {})'.format(v, str(
|
|
202
|
-
self._conf_items[k]) if k in self._conf_items else '<not set>')
|
|
203
|
-
else:
|
|
204
|
-
prt[' ' + k] = v
|
|
205
|
-
if self._conf_section is not None:
|
|
206
|
-
s += ' ' * 4 + 'configuration: ' + self._conf_section + '\n'
|
|
207
|
-
s += dict_as_str(prt, prefix=4)
|
|
208
|
-
return s.strip()
|
|
147
|
+
def tune(self, **kw):
|
|
148
|
+
self._tuning.update(kw)
|
|
209
149
|
|
|
210
150
|
def getx(self, key, *args, **kw):
|
|
211
151
|
"""Shortcut to access the configuration overridden by the tuning."""
|
|
212
152
|
if key in self._tuning:
|
|
213
153
|
return self._tuning[key]
|
|
214
154
|
|
|
215
|
-
if
|
|
216
|
-
return
|
|
155
|
+
if "default" in kw:
|
|
156
|
+
return kw["default"]
|
|
217
157
|
|
|
218
|
-
|
|
219
|
-
return kw['default']
|
|
220
|
-
|
|
221
|
-
raise KeyError('The "{:s}" entry was not found in any configuration'.format(key))
|
|
158
|
+
raise KeyError('The "{:s}" entry was not found'.format(key))
|
|
222
159
|
|
|
223
160
|
|
|
224
161
|
class SendMail(Action):
|
|
@@ -226,11 +163,14 @@ class SendMail(Action):
|
|
|
226
163
|
Class responsible for sending emails.
|
|
227
164
|
"""
|
|
228
165
|
|
|
229
|
-
def __init__(
|
|
166
|
+
def __init__(
|
|
167
|
+
self, kind="mail", service="sendmail", active=True, quoteprintable=True
|
|
168
|
+
):
|
|
230
169
|
super().__init__(kind=kind, active=active, service=service)
|
|
231
170
|
if quoteprintable:
|
|
232
171
|
from email import charset
|
|
233
|
-
|
|
172
|
+
|
|
173
|
+
charset.add_charset("utf-8", charset.QP, charset.QP, "utf-8")
|
|
234
174
|
|
|
235
175
|
|
|
236
176
|
class TemplatedMail(TunableAction):
|
|
@@ -238,18 +178,24 @@ class TemplatedMail(TunableAction):
|
|
|
238
178
|
|
|
239
179
|
Do not use directly !
|
|
240
180
|
"""
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
181
|
+
|
|
182
|
+
def __init__(
|
|
183
|
+
self,
|
|
184
|
+
catalog,
|
|
185
|
+
kind="templatedmail",
|
|
186
|
+
service="templatedmail",
|
|
187
|
+
active=True,
|
|
188
|
+
inputs_charset=None,
|
|
189
|
+
):
|
|
190
|
+
super().__init__(
|
|
191
|
+
configuration=None, kind=kind, active=active, service=service
|
|
192
|
+
)
|
|
247
193
|
self.inputs_charset = inputs_charset
|
|
248
194
|
|
|
249
195
|
def service_info(self, **kw):
|
|
250
196
|
"""Kindly propose the permanent directory and catalog to the final service"""
|
|
251
|
-
kw.setdefault(
|
|
252
|
-
kw.setdefault(
|
|
197
|
+
kw.setdefault("catalog", self.catalog)
|
|
198
|
+
kw.setdefault("inputs_charset", self.inputs_charset)
|
|
253
199
|
return super().service_info(**kw)
|
|
254
200
|
|
|
255
201
|
def execute(self, *args, **kw):
|
|
@@ -261,7 +207,11 @@ class TemplatedMail(TunableAction):
|
|
|
261
207
|
rc = None
|
|
262
208
|
service = self.get_active_service(**kw)
|
|
263
209
|
if service:
|
|
264
|
-
options = {
|
|
210
|
+
options = {
|
|
211
|
+
k: v
|
|
212
|
+
for k, v in kw.items()
|
|
213
|
+
if k not in service.footprint_attributes
|
|
214
|
+
}
|
|
265
215
|
rc = service(options)
|
|
266
216
|
return rc
|
|
267
217
|
|
|
@@ -271,7 +221,7 @@ class Report(TunableAction):
|
|
|
271
221
|
Class responsible for sending reports.
|
|
272
222
|
"""
|
|
273
223
|
|
|
274
|
-
def __init__(self, kind=
|
|
224
|
+
def __init__(self, kind="report", service="sendreport", active=True):
|
|
275
225
|
super().__init__(kind=kind, active=active, service=service)
|
|
276
226
|
|
|
277
227
|
|
|
@@ -280,7 +230,7 @@ class SSH(Action):
|
|
|
280
230
|
Class responsible for sending commands to an SSH proxy.
|
|
281
231
|
"""
|
|
282
232
|
|
|
283
|
-
def __init__(self, kind=
|
|
233
|
+
def __init__(self, kind="ssh", service="ssh", active=True):
|
|
284
234
|
super().__init__(kind=kind, active=active, service=service)
|
|
285
235
|
|
|
286
236
|
|
|
@@ -289,17 +239,23 @@ class AskJeeves(TunableAction):
|
|
|
289
239
|
Class responsible for posting requests to Jeeves daemon.
|
|
290
240
|
"""
|
|
291
241
|
|
|
292
|
-
def __init__(self, kind=
|
|
293
|
-
super().__init__(
|
|
242
|
+
def __init__(self, kind="jeeves", service="askjeeves", active=True):
|
|
243
|
+
super().__init__(
|
|
244
|
+
configuration=None, kind=kind, active=active, service=service
|
|
245
|
+
)
|
|
294
246
|
|
|
295
247
|
def execute(self, *args, **kw):
|
|
296
248
|
"""Generic method to perform the action through a service."""
|
|
297
249
|
rc = None
|
|
298
|
-
if
|
|
299
|
-
kw[
|
|
250
|
+
if "kind" in kw:
|
|
251
|
+
kw["fwd_kind"] = kw.pop("kind")
|
|
300
252
|
service = self.get_active_service(**kw)
|
|
301
253
|
if service:
|
|
302
|
-
talk = {
|
|
254
|
+
talk = {
|
|
255
|
+
k: v
|
|
256
|
+
for k, v in kw.items()
|
|
257
|
+
if k not in service.footprint_attributes
|
|
258
|
+
}
|
|
303
259
|
rc = service(talk)
|
|
304
260
|
return rc
|
|
305
261
|
|
|
@@ -309,23 +265,27 @@ class Prompt(Action):
|
|
|
309
265
|
Fake action that could be used for any real action.
|
|
310
266
|
"""
|
|
311
267
|
|
|
312
|
-
def __init__(self, kind=
|
|
268
|
+
def __init__(self, kind="prompt", service="prompt", active=True):
|
|
313
269
|
super().__init__(kind=kind, active=active, service=service)
|
|
314
270
|
|
|
315
271
|
def execute(self, *args, **kw):
|
|
316
272
|
"""Do nothing but prompt the actual arguments."""
|
|
317
273
|
# kind could be unintentionally given, force it back
|
|
318
|
-
kw[
|
|
274
|
+
kw["kind"] = self.kind
|
|
319
275
|
service = self.get_active_service(**kw)
|
|
320
276
|
rc = False
|
|
321
277
|
if service:
|
|
322
|
-
options = {
|
|
278
|
+
options = {
|
|
279
|
+
k: v
|
|
280
|
+
for k, v in kw.items()
|
|
281
|
+
if k not in service.footprint_attributes
|
|
282
|
+
}
|
|
323
283
|
rc = service(options)
|
|
324
284
|
return rc
|
|
325
285
|
|
|
326
286
|
def foo(self, *args, **kw):
|
|
327
287
|
"""Yet an other foo method."""
|
|
328
|
-
print(
|
|
288
|
+
print("#FOO", self.kind, "/ args:", args, "/ kw:", kw)
|
|
329
289
|
return True
|
|
330
290
|
|
|
331
291
|
|
|
@@ -334,32 +294,55 @@ class FlowSchedulerGateway(Action):
|
|
|
334
294
|
Send a child command to any ECMWF's workfow scheduler.
|
|
335
295
|
"""
|
|
336
296
|
|
|
337
|
-
_KNOWN_CMD = dict(
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
297
|
+
_KNOWN_CMD = dict(
|
|
298
|
+
sms=[
|
|
299
|
+
"abort",
|
|
300
|
+
"complete",
|
|
301
|
+
"event",
|
|
302
|
+
"init",
|
|
303
|
+
"label",
|
|
304
|
+
"meter",
|
|
305
|
+
"msg",
|
|
306
|
+
"variable",
|
|
307
|
+
"fix",
|
|
308
|
+
],
|
|
309
|
+
ecflow=["abort", "complete", "event", "init", "label", "meter", "msg"],
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
def __init__(self, kind="flow", service=None, active=True, permanent=True):
|
|
341
313
|
"""
|
|
342
314
|
The `service` attribute must be specified (it can be either sms or ecflow).
|
|
343
315
|
"""
|
|
344
316
|
if service is None:
|
|
345
|
-
raise ValueError(
|
|
346
|
-
super().__init__(
|
|
317
|
+
raise ValueError("The service name must be provided")
|
|
318
|
+
super().__init__(
|
|
319
|
+
kind=kind, active=active, service=service, permanent=permanent
|
|
320
|
+
)
|
|
347
321
|
|
|
348
322
|
def gateway(self, *args, **kw):
|
|
349
323
|
"""Ask the Scheduler to run any (but known) command."""
|
|
350
324
|
rc = None
|
|
351
325
|
service = self.get_active_service(**kw)
|
|
352
326
|
if service and self._schedcmd is not None:
|
|
353
|
-
kwbis = {k: v for k, v in kw.items() if k in (
|
|
327
|
+
kwbis = {k: v for k, v in kw.items() if k in ("critical",)}
|
|
354
328
|
rc = getattr(service, self._schedcmd)(*args, **kwbis)
|
|
355
329
|
self._schedcmd = None
|
|
356
330
|
return rc
|
|
357
331
|
|
|
358
332
|
def __getattr__(self, attr):
|
|
359
|
-
if attr.startswith(
|
|
333
|
+
if attr.startswith("_"):
|
|
360
334
|
raise AttributeError
|
|
361
|
-
if attr in (
|
|
362
|
-
|
|
335
|
+
if attr in (
|
|
336
|
+
[
|
|
337
|
+
"conf",
|
|
338
|
+
"info",
|
|
339
|
+
"clear",
|
|
340
|
+
"mute",
|
|
341
|
+
"play",
|
|
342
|
+
"path",
|
|
343
|
+
]
|
|
344
|
+
+ self._KNOWN_CMD[self.service]
|
|
345
|
+
):
|
|
363
346
|
self._schedcmd = attr
|
|
364
347
|
return self.gateway
|
|
365
348
|
else:
|
|
@@ -370,15 +353,21 @@ class FlowSchedulerGateway(Action):
|
|
|
370
353
|
class SmsGateway(FlowSchedulerGateway):
|
|
371
354
|
"""Send a child command to an SMS server."""
|
|
372
355
|
|
|
373
|
-
def __init__(self, kind=
|
|
374
|
-
super().__init__(
|
|
356
|
+
def __init__(self, kind="sms", service="sms", active=True, permanent=True):
|
|
357
|
+
super().__init__(
|
|
358
|
+
kind=kind, active=active, service=service, permanent=permanent
|
|
359
|
+
)
|
|
375
360
|
|
|
376
361
|
|
|
377
362
|
class EcflowGateway(FlowSchedulerGateway):
|
|
378
363
|
"""Send a child command to an Ecflow server."""
|
|
379
364
|
|
|
380
|
-
def __init__(
|
|
381
|
-
|
|
365
|
+
def __init__(
|
|
366
|
+
self, kind="ecflow", service="ecflow", active=True, permanent=True
|
|
367
|
+
):
|
|
368
|
+
super().__init__(
|
|
369
|
+
kind=kind, active=active, service=service, permanent=permanent
|
|
370
|
+
)
|
|
382
371
|
|
|
383
372
|
|
|
384
373
|
class SpooledActions:
|
|
@@ -425,7 +414,7 @@ class Dispatcher(bronx.stdtypes.catalog.Catalog):
|
|
|
425
414
|
"""
|
|
426
415
|
|
|
427
416
|
def __init__(self, **kw):
|
|
428
|
-
logger.debug(
|
|
417
|
+
logger.debug("Action dispatcher init %s", self)
|
|
429
418
|
super().__init__(**kw)
|
|
430
419
|
|
|
431
420
|
@property
|
|
@@ -444,11 +433,11 @@ class Dispatcher(bronx.stdtypes.catalog.Catalog):
|
|
|
444
433
|
self.discard(item)
|
|
445
434
|
|
|
446
435
|
def __getattr__(self, attr):
|
|
447
|
-
if attr.startswith(
|
|
436
|
+
if attr.startswith("_"):
|
|
448
437
|
raise AttributeError
|
|
449
|
-
a_kind, u_sep, a_method = attr.partition(
|
|
438
|
+
a_kind, u_sep, a_method = attr.partition("_")
|
|
450
439
|
if not a_method:
|
|
451
|
-
a_method =
|
|
440
|
+
a_method = "execute"
|
|
452
441
|
return SpooledActions(a_kind, a_method, self.candidates(a_kind))
|
|
453
442
|
|
|
454
443
|
|