vortex-nwp 2.1.1__tar.gz → 2.1.3__tar.gz
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_nwp-2.1.1 → vortex_nwp-2.1.3}/PKG-INFO +1 -1
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/pyproject.toml +1 -1
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/__init__.py +2 -2
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/algo/components.py +4 -1
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/algo/mpitools.py +10 -6
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/data/abstractstores.py +8 -1
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/data/geometries.py +13 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/data/providers.py +4 -3
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/data/stores.py +15 -20
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/stores.py +11 -7
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/grib.py +2 -2
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/schedulers.py +36 -41
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/services.py +21 -7
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex_nwp.egg-info/PKG-INFO +1 -1
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex_nwp.egg-info/SOURCES.txt +2 -0
- vortex_nwp-2.1.3/tests/test_ecflow.py +37 -0
- vortex_nwp-2.1.3/tests/test_geometries.py +170 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/LICENSE +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/README.md +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/setup.cfg +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/algo/__init__.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/algo/mpitools_templates/__init__.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/algo/mpitools_templates/envelope_wrapper_default.tpl +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/algo/mpitools_templates/envelope_wrapper_mpiauto.tpl +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/algo/mpitools_templates/wrapstd_wrapper_default.tpl +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/algo/serversynctools.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/config.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/data/__init__.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/data/containers.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/data/contents.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/data/executables.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/data/flow.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/data/geometries.ini +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/data/handlers.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/data/outflow.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/data/resources.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/data/sync_templates/__init__.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/gloves.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/layout/__init__.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/layout/contexts.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/layout/dataflow.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/layout/monitor.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/__init__.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/algo/__init__.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/algo/assim.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/algo/clim.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/algo/coupling.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/algo/eda.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/algo/eps.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/algo/forecasts.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/algo/fpserver.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/algo/ifsnaming.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/algo/ifsroot.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/algo/monitoring.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/algo/mpitools.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/algo/odbtools.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/algo/oopsroot.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/algo/oopstests.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/algo/request.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/algo/stdpost.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/__init__.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/assim.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/boundaries.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/climfiles.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/configfiles.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/consts.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/ctpini.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/diagnostics.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/eda.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/eps.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/executables.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/fields.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/gridfiles.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/logs.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/modelstates.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/monitoring.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/namelists.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/obs.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/oopsexec.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/providers.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/query.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/data/surfex.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/syntax/__init__.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/syntax/stdattrs.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/tools/__init__.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/tools/addons.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/tools/agt.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/tools/bdap.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/tools/bdcp.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/tools/bdm.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/tools/bdmp.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/tools/conftools.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/tools/drhook.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/tools/grib.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/tools/gribdiff.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/tools/ifstools.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/tools/igastuff.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/tools/mars.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/tools/odb.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/tools/partitioning.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/tools/satrad.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/util/__init__.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/util/async.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/util/beacon.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/util/diffpygram.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/util/ens.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/util/hooks.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/util/taskdeco.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/util/usepygram.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/nwp/util/usetnt.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/proxy.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/sessions.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/syntax/__init__.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/syntax/stdattrs.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/syntax/stddeco.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/toolbox.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/__init__.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/actions.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/addons.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/arm.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/compression.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/date.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/ddhpack.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/delayedactions.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/env.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/folder.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/lfi.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/listings.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/names.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/net.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/odb.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/parallelism.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/prestaging.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/rawfiles.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/storage.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/surfex.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/systems.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/tools/targets.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/util/__init__.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/util/config.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/util/empty.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/util/helpers.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/util/introspection.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/util/iosponge.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/util/roles.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/util/storefunctions.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/util/structs.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex/util/worker.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex_nwp.egg-info/dependency_links.txt +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex_nwp.egg-info/requires.txt +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/src/vortex_nwp.egg-info/top_level.txt +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_algo_server.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_arpifs_listings_integration.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_cfgparser.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_cfgtemplating.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_compression.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_config.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_conftools.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_containers.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_doctests.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_ecmwf_interface.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_env.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_epygram.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_gco.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_ifstools.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_import.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_iosponge.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_job_examples.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_layoutappconf.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_layoutjobs.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_layoutmonitor.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_layoutnodes.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_net_netstat.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_net_ssh.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_partitioning.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_providers.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_sessions_stuff.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_simpleworkflow.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_smartftget.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_ssh.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_storage.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_stores.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_syntax.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_targets.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_templates.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_toolsodb.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_uget.py +0 -0
- {vortex_nwp-2.1.1 → vortex_nwp-2.1.3}/tests/test_vortexnames.py +0 -0
|
@@ -53,10 +53,10 @@ from .toolbox import algo as task
|
|
|
53
53
|
|
|
54
54
|
from . import nwp as nwp # footprints import
|
|
55
55
|
|
|
56
|
-
__version__ = "2.1.
|
|
56
|
+
__version__ = "2.1.3"
|
|
57
57
|
__prompt__ = "Vortex v-" + __version__ + ":"
|
|
58
58
|
|
|
59
|
-
__nextversion__ = "2.1.
|
|
59
|
+
__nextversion__ = "2.1.4"
|
|
60
60
|
__tocinfoline__ = "VORTEX core package"
|
|
61
61
|
|
|
62
62
|
__all__ = [
|
|
@@ -1841,7 +1841,10 @@ class Parallel(xExecutableAlgoComponent):
|
|
|
1841
1841
|
def _mpitool_attributes(self, opts):
|
|
1842
1842
|
"""Return the dictionary of attributes needed to create the mpitool object."""
|
|
1843
1843
|
# Read the appropriate configuration in the target file
|
|
1844
|
-
|
|
1844
|
+
if not config.is_defined(section="mpitool"):
|
|
1845
|
+
conf_dict = {}
|
|
1846
|
+
else:
|
|
1847
|
+
conf_dict = config.from_config(section="mpitool")
|
|
1845
1848
|
if self.mpiname:
|
|
1846
1849
|
conf_dict["mpiname"] = self.mpiname
|
|
1847
1850
|
# Make "mpirun" the default mpi command name
|
|
@@ -84,6 +84,7 @@ from vortex.tools import env
|
|
|
84
84
|
from vortex.tools.arm import ArmForgeTool
|
|
85
85
|
from vortex.tools.systems import ExecutionError
|
|
86
86
|
from vortex.util import config
|
|
87
|
+
from vortex.config import is_defined, ConfigurationError
|
|
87
88
|
|
|
88
89
|
#: No automatic export
|
|
89
90
|
__all__ = []
|
|
@@ -1431,12 +1432,15 @@ class SRun(MpiTool):
|
|
|
1431
1432
|
@property
|
|
1432
1433
|
def _actual_slurmversion(self):
|
|
1433
1434
|
"""Return the slurm major version number."""
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
if not slurmversion:
|
|
1438
|
-
raise
|
|
1439
|
-
|
|
1435
|
+
if self.slurmversion:
|
|
1436
|
+
return self.slurmversion
|
|
1437
|
+
|
|
1438
|
+
if not is_defined(section="mpitool", key="slurmversion"):
|
|
1439
|
+
raise ConfigurationError(
|
|
1440
|
+
"Using 'srun' MPI tool but slurm version is not configured. See "
|
|
1441
|
+
"https://vortex-nwp.readthedocs.io/en/latest/user-guide/configuration.html#mpitool"
|
|
1442
|
+
)
|
|
1443
|
+
return from_config(section="mpitool", key="slurmversion")
|
|
1440
1444
|
|
|
1441
1445
|
def _set_binaries_hack(self, binaries):
|
|
1442
1446
|
"""Set the list of :class:`MpiBinaryDescription` objects associated with this instance."""
|
|
@@ -14,7 +14,7 @@ from bronx.system import hash as hashutils
|
|
|
14
14
|
import footprints
|
|
15
15
|
|
|
16
16
|
from vortex import sessions
|
|
17
|
-
from vortex.config import from_config, ConfigurationError
|
|
17
|
+
from vortex.config import from_config, ConfigurationError, is_defined
|
|
18
18
|
from vortex.syntax.stdattrs import (
|
|
19
19
|
hashalgo,
|
|
20
20
|
hashalgo_avail_list,
|
|
@@ -870,6 +870,13 @@ class ArchiveStore(Store):
|
|
|
870
870
|
@property
|
|
871
871
|
def actual_storetube(self):
|
|
872
872
|
"""This archive network name (potentially read form the configuration file)."""
|
|
873
|
+
if self._actual_storetube:
|
|
874
|
+
return self._actual_storetube
|
|
875
|
+
if not is_defined(section="storage", key="protocol"):
|
|
876
|
+
raise ConfigurationError(
|
|
877
|
+
"Using remote data tree but protocol is not configured. See "
|
|
878
|
+
"https://vortex-nwp.readthedocs.io/en/latest/user-guide/configuration.html#storage"
|
|
879
|
+
)
|
|
873
880
|
if self._actual_storetube is None:
|
|
874
881
|
self._actual_storetube = from_config(
|
|
875
882
|
section="storage",
|
|
@@ -764,12 +764,25 @@ def load(inifile="@geometries.ini", refresh=False, verbose=True):
|
|
|
764
764
|
|
|
765
765
|
The class that will be instantiated depends on the "kind" keyword..
|
|
766
766
|
"""
|
|
767
|
+
from vortex import sessions
|
|
768
|
+
|
|
767
769
|
iniconf = configparser.ConfigParser()
|
|
770
|
+
|
|
771
|
+
# Load from vortex distribution
|
|
768
772
|
with importlib.resources.open_text(
|
|
769
773
|
"vortex.data",
|
|
770
774
|
"geometries.ini",
|
|
771
775
|
) as fh:
|
|
772
776
|
iniconf.read_file(fh)
|
|
777
|
+
|
|
778
|
+
# Load from user's config directory if it exists
|
|
779
|
+
glove = sessions.current().glove
|
|
780
|
+
local = sessions.system()
|
|
781
|
+
user_geometries = glove.configrc + "/geometries.ini"
|
|
782
|
+
if local.path.exists(user_geometries):
|
|
783
|
+
with open(user_geometries, encoding="utf-8") as fh:
|
|
784
|
+
iniconf.read_file(fh)
|
|
785
|
+
|
|
773
786
|
for item in iniconf.sections():
|
|
774
787
|
gdesc = dict(iniconf.items(item))
|
|
775
788
|
gkind = gdesc.get("kind")
|
|
@@ -279,6 +279,7 @@ class Vortex(Provider):
|
|
|
279
279
|
|
|
280
280
|
_DEFAULT_NAME_BUILDER = names.VortexNameBuilder()
|
|
281
281
|
_CUSTOM_NAME_BUILDERS = dict()
|
|
282
|
+
_SPECIAL_EXPS = ("oper", "dble", "test", "mirr")
|
|
282
283
|
|
|
283
284
|
_footprint = [
|
|
284
285
|
block,
|
|
@@ -365,8 +366,8 @@ class Vortex(Provider):
|
|
|
365
366
|
self._namebuilder = self._CUSTOM_NAME_BUILDERS[self.namebuild]
|
|
366
367
|
else:
|
|
367
368
|
self._namebuilder = self._DEFAULT_NAME_BUILDER
|
|
368
|
-
if self.experiment in (
|
|
369
|
-
self.experiment = self.experiment.
|
|
369
|
+
if self.experiment in (n.upper() for n in self._SPECIAL_EXPS):
|
|
370
|
+
self.experiment = self.experiment.lower()
|
|
370
371
|
|
|
371
372
|
# Ensure compatibility with deprecated namespace attribute
|
|
372
373
|
# Under the hood the namespace attribute is still used to
|
|
@@ -426,7 +427,7 @@ class Vortex(Provider):
|
|
|
426
427
|
|
|
427
428
|
def netloc(self, resource):
|
|
428
429
|
"""Returns the current ``namespace``."""
|
|
429
|
-
if self.experiment in
|
|
430
|
+
if self.experiment in self._SPECIAL_EXPS:
|
|
430
431
|
return "vsop." + self.namespace.domain
|
|
431
432
|
return self.namespace.netloc
|
|
432
433
|
|
|
@@ -908,16 +908,9 @@ class VortexArchiveStore(MultiStore):
|
|
|
908
908
|
|
|
909
909
|
def alternates_netloc(self):
|
|
910
910
|
"""Return netlocs describing both base and stacked archives."""
|
|
911
|
-
netloc_m = re.match(
|
|
912
|
-
r"(?P<base>v.*)\.archive\.(?P<country>\w+)", self.netloc
|
|
913
|
-
)
|
|
914
911
|
return [
|
|
915
|
-
"{
|
|
916
|
-
|
|
917
|
-
),
|
|
918
|
-
"{base:s}.stacked-archive-legacy.{country:s}".format(
|
|
919
|
-
**netloc_m.groupdict()
|
|
920
|
-
),
|
|
912
|
+
f"{self.netloc.firstname}.archive-legacy.fr",
|
|
913
|
+
f"{self.netloc.firstname}.stacked-archive-legacy.fr",
|
|
921
914
|
]
|
|
922
915
|
|
|
923
916
|
def alternates_fpextras(self):
|
|
@@ -1025,18 +1018,16 @@ class VortexCacheOp2ResearchStore(_VortexCacheBaseStore):
|
|
|
1025
1018
|
|
|
1026
1019
|
def __init__(self, *args, **kw):
|
|
1027
1020
|
super().__init__(*args, **kw)
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
except config.ConfigurationError as e:
|
|
1034
|
-
logger.error(
|
|
1035
|
-
"Cannot use special experiment cache without providing",
|
|
1036
|
-
"cache location",
|
|
1021
|
+
if not config.is_defined(section="data-tree", key="op_rootdir"):
|
|
1022
|
+
raise config.ConfigurationError(
|
|
1023
|
+
"Using special experiment but corresponding cache location "
|
|
1024
|
+
'is not configured. Bet sure to set "op_rootdir" in configuration. '
|
|
1025
|
+
"See https://vortex-nwp.readthedocs.io/en/latest/user-guide/oper-dble-data-trees"
|
|
1037
1026
|
)
|
|
1038
|
-
|
|
1039
|
-
|
|
1027
|
+
cachepath = config.from_config(
|
|
1028
|
+
section="data-tree",
|
|
1029
|
+
key="op_rootdir",
|
|
1030
|
+
)
|
|
1040
1031
|
self.location = os.path.join(cachepath, "vortex")
|
|
1041
1032
|
|
|
1042
1033
|
|
|
@@ -1341,6 +1332,10 @@ class PromiseCacheStore(VortexCacheMtStore):
|
|
|
1341
1332
|
),
|
|
1342
1333
|
)
|
|
1343
1334
|
|
|
1335
|
+
def __init__(self, *args, **kw):
|
|
1336
|
+
super().__init__(*args, **kw)
|
|
1337
|
+
self.location = os.path.join(get_cache_location(), "promise")
|
|
1338
|
+
|
|
1344
1339
|
@staticmethod
|
|
1345
1340
|
def _add_default_options(options):
|
|
1346
1341
|
options_upd = options.copy()
|
|
@@ -12,6 +12,7 @@ from bronx.fancies import loggers
|
|
|
12
12
|
from bronx.stdtypes import date
|
|
13
13
|
from vortex.data.abstractstores import Store
|
|
14
14
|
from vortex.syntax.stdattrs import compressionpipeline
|
|
15
|
+
from vortex import config
|
|
15
16
|
|
|
16
17
|
#: No automatic export
|
|
17
18
|
__all__ = []
|
|
@@ -109,14 +110,17 @@ class BdpeStore(Store):
|
|
|
109
110
|
if s_archive == "True":
|
|
110
111
|
extraenv["BDPE_LECTURE_ARCHIVE_AUTORISEE"] = "oui"
|
|
111
112
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
wscommand = self.system.default_target.get("bdpe:wsclient_path", None)
|
|
116
|
-
if wscommand is None:
|
|
117
|
-
raise RuntimeError(
|
|
118
|
-
"bdpe:wsclient_path has to be set in the target config"
|
|
113
|
+
try:
|
|
114
|
+
wsinterpreter = config.from_config(
|
|
115
|
+
section="bdpe", key="wsclient_interpreter"
|
|
119
116
|
)
|
|
117
|
+
except config.ConfigurationError:
|
|
118
|
+
wsinterpreter = "bash"
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
wscommand = config.from_config(section="bdpe", key="wsclient_path")
|
|
122
|
+
except config.ConfigurationError:
|
|
123
|
+
wscommand = "/opt/softs/sopra/bin/lirepe.sh"
|
|
120
124
|
|
|
121
125
|
args.insert(0, wscommand)
|
|
122
126
|
if wsinterpreter is not None:
|
|
@@ -567,7 +567,7 @@ class EcGribDecoMixin(AlgoComponentDecoMixin):
|
|
|
567
567
|
def _eccodes_envsetup(
|
|
568
568
|
self,
|
|
569
569
|
eccodes_lib,
|
|
570
|
-
envvar="
|
|
570
|
+
envvar="ECCODES_DEFINITION_PATH",
|
|
571
571
|
tgt_path="definitions",
|
|
572
572
|
):
|
|
573
573
|
"""Export envirionment variables required by ECCODES
|
|
@@ -612,7 +612,7 @@ class EcGribDecoMixin(AlgoComponentDecoMixin):
|
|
|
612
612
|
if eccodes_lib is not None:
|
|
613
613
|
defvar = self._eccodes_envsetup(
|
|
614
614
|
eccodes_lib,
|
|
615
|
-
envvar="
|
|
615
|
+
envvar="ECCODES_DEFINITION_PATH",
|
|
616
616
|
tgt_path="definitions",
|
|
617
617
|
)
|
|
618
618
|
subdir = Path("ifs_samples") / (
|
|
@@ -4,12 +4,14 @@ Interface to SMS commands.
|
|
|
4
4
|
|
|
5
5
|
import contextlib
|
|
6
6
|
import functools
|
|
7
|
+
import re
|
|
8
|
+
import socket
|
|
7
9
|
|
|
8
10
|
from bronx.fancies import loggers
|
|
9
11
|
import footprints
|
|
10
12
|
|
|
11
13
|
from vortex import config
|
|
12
|
-
from .services import Service
|
|
14
|
+
from .services import Service, get_cluster_name
|
|
13
15
|
|
|
14
16
|
__all__ = []
|
|
15
17
|
|
|
@@ -363,46 +365,40 @@ class EcFlow(EcmwfLikeScheduler):
|
|
|
363
365
|
def __init__(self, *args, **kw):
|
|
364
366
|
logger.debug("EcFlow scheduler client init %s", self)
|
|
365
367
|
super().__init__(*args, **kw)
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
"""Return the actual binary path to the EcFlow client."""
|
|
370
|
-
if self._actual_clientpath is None:
|
|
371
|
-
thistarget = self.sh.default_target
|
|
372
|
-
guesspath = self.env.ECF_CLIENT_PATH or thistarget.get(
|
|
373
|
-
"ecflow:clientpath"
|
|
374
|
-
)
|
|
375
|
-
ecfversion = self.env.get("ECF_VERSION", "default")
|
|
376
|
-
guesspath = guesspath.format(version=ecfversion)
|
|
377
|
-
if guesspath is None:
|
|
378
|
-
logger.warning(
|
|
379
|
-
"ecFlow service could not guess the install location [%s]",
|
|
380
|
-
str(guesspath),
|
|
381
|
-
)
|
|
368
|
+
if not self.clientpath:
|
|
369
|
+
if not config.is_defined(section="ecflow", key="clientpath"):
|
|
370
|
+
self.clientpath = "ecflow_client"
|
|
382
371
|
else:
|
|
383
|
-
self.
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
self._actual_clientpath,
|
|
388
|
-
)
|
|
389
|
-
return self._actual_clientpath
|
|
372
|
+
self.clientpath = config.from_config(
|
|
373
|
+
section="ecflow",
|
|
374
|
+
key="clientpath",
|
|
375
|
+
)
|
|
390
376
|
|
|
391
377
|
@contextlib.contextmanager
|
|
392
378
|
def child_session_setup(self):
|
|
393
379
|
"""Setup a SSH tunnel if necessary."""
|
|
394
380
|
with super().child_session_setup() as setup_rc:
|
|
395
|
-
|
|
381
|
+
name = get_cluster_name(socket.gethostname())
|
|
382
|
+
# If the current node is a compute node, it cannot reach
|
|
383
|
+
# the EcFlow server. In this case, the request is made
|
|
384
|
+
# through a SSH tunnel on taranisoper-int
|
|
385
|
+
is_compute_node = re.match(
|
|
386
|
+
rf"{name}\d+\.{name}hpc\.meteo\.fr", socket.gethostname()
|
|
387
|
+
)
|
|
388
|
+
if setup_rc and is_compute_node:
|
|
396
389
|
tunnel = None
|
|
397
390
|
# wait and retries from config
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
391
|
+
ssh_settings = {
|
|
392
|
+
conf_key: default
|
|
393
|
+
if not config.is_defined("ecflow", conf_key)
|
|
394
|
+
else config.from_config("ecflow", conf_key)
|
|
395
|
+
for conf_key, default in (
|
|
396
|
+
("sshproxy_wait", 6),
|
|
397
|
+
("sshproxy_retries", 2),
|
|
398
|
+
("sshproxy_retrydelay", 1),
|
|
399
|
+
)
|
|
400
|
+
}
|
|
401
|
+
|
|
406
402
|
# Build up an SSH tunnel to convey the EcFlow command
|
|
407
403
|
ecconf = self.conf(dict())
|
|
408
404
|
echost = ecconf.get("{:s}HOST".format(self.env_pattern), None)
|
|
@@ -411,14 +407,15 @@ class EcFlow(EcmwfLikeScheduler):
|
|
|
411
407
|
setup_rc = False
|
|
412
408
|
else:
|
|
413
409
|
sshobj = self.sh.ssh(
|
|
414
|
-
"
|
|
415
|
-
virtualnode=True,
|
|
410
|
+
hostname=f"{name}oper-int",
|
|
416
411
|
mandatory_hostcheck=False,
|
|
417
|
-
maxtries=
|
|
418
|
-
triesdelay=
|
|
412
|
+
maxtries=ssh_settings["sshproxy_retries"],
|
|
413
|
+
triesdelay=ssh_settings["sshproxy_retrydelay"],
|
|
419
414
|
)
|
|
420
415
|
tunnel = sshobj.tunnel(
|
|
421
|
-
echost,
|
|
416
|
+
echost,
|
|
417
|
+
int(ecport),
|
|
418
|
+
maxwait=ssh_settings["sshproxy_wait"],
|
|
422
419
|
)
|
|
423
420
|
if not tunnel:
|
|
424
421
|
setup_rc = False
|
|
@@ -458,9 +455,7 @@ class EcFlow(EcmwfLikeScheduler):
|
|
|
458
455
|
|
|
459
456
|
def _actual_child(self, cmd, options, critical=True):
|
|
460
457
|
"""Miscellaneous ecFlow sub-command."""
|
|
461
|
-
args = [
|
|
462
|
-
self.path(),
|
|
463
|
-
]
|
|
458
|
+
args = [self.clientpath]
|
|
464
459
|
if options:
|
|
465
460
|
args.append("--{:s}={!s}".format(cmd, options[0]))
|
|
466
461
|
if len(options) > 1:
|
|
@@ -9,6 +9,7 @@ import configparser
|
|
|
9
9
|
import contextlib
|
|
10
10
|
import hashlib
|
|
11
11
|
import pprint
|
|
12
|
+
import re
|
|
12
13
|
from string import Template
|
|
13
14
|
|
|
14
15
|
|
|
@@ -23,6 +24,7 @@ from vortex.util.config import (
|
|
|
23
24
|
load_template,
|
|
24
25
|
LegacyTemplatingAdapter,
|
|
25
26
|
)
|
|
27
|
+
from vortex import config
|
|
26
28
|
|
|
27
29
|
#: No automatic export
|
|
28
30
|
__all__ = []
|
|
@@ -530,8 +532,6 @@ class HideService(Service):
|
|
|
530
532
|
"""Main action: hide a cheap copy of this file under a unique name."""
|
|
531
533
|
|
|
532
534
|
rootdir = self.rootdir
|
|
533
|
-
if rootdir is None:
|
|
534
|
-
rootdir = self.sh.default_target.get("hidden_rootdir", None)
|
|
535
535
|
if rootdir is not None:
|
|
536
536
|
rootdir = self.sh.path.expanduser(rootdir)
|
|
537
537
|
|
|
@@ -795,12 +795,11 @@ class TemplatedMailService(MailService):
|
|
|
795
795
|
"""
|
|
796
796
|
tpl = self.message
|
|
797
797
|
if tpl == "":
|
|
798
|
-
|
|
799
|
-
|
|
798
|
+
tplpath = self._TEMPLATES_DIR / (
|
|
799
|
+
self.section.get("template", self.id) + ".tpl"
|
|
800
|
+
)
|
|
800
801
|
try:
|
|
801
|
-
tpl = load_template(
|
|
802
|
-
self.ticket, tplfile, encoding=self.inputs_charset
|
|
803
|
-
)
|
|
802
|
+
tpl = load_template(tplpath, encoding=self.inputs_charset)
|
|
804
803
|
except ValueError as exc:
|
|
805
804
|
logger.error("%s", exc.message)
|
|
806
805
|
return None
|
|
@@ -938,3 +937,18 @@ class AbstractRdTemplatedMailService(TemplatedMailService):
|
|
|
938
937
|
# The generic host/cluster name
|
|
939
938
|
sdict["host"] = self.sh.default_target.inetname
|
|
940
939
|
return sdict
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
def get_cluster_name(hostname):
|
|
943
|
+
if not config.is_defined(section="services", key="cluster_names"):
|
|
944
|
+
raise config.ConfigurationError(
|
|
945
|
+
'Missing configuration key "cluster_names" in section "services". '
|
|
946
|
+
"See https://vortex-nwp.readthedocs.io/en/latest/user-guide/configuration.html#services"
|
|
947
|
+
)
|
|
948
|
+
cluster_names = config.from_config(section="services", key="cluster_names")
|
|
949
|
+
m = re.match("^(" + "|".join(n for n in cluster_names) + ")", hostname)
|
|
950
|
+
if (m is None) or (m.group(0) not in cluster_names):
|
|
951
|
+
raise ValueError(
|
|
952
|
+
f"Current host should be either one of {cluster_names}"
|
|
953
|
+
)
|
|
954
|
+
return m.group(0)
|
|
@@ -154,10 +154,12 @@ tests/test_config.py
|
|
|
154
154
|
tests/test_conftools.py
|
|
155
155
|
tests/test_containers.py
|
|
156
156
|
tests/test_doctests.py
|
|
157
|
+
tests/test_ecflow.py
|
|
157
158
|
tests/test_ecmwf_interface.py
|
|
158
159
|
tests/test_env.py
|
|
159
160
|
tests/test_epygram.py
|
|
160
161
|
tests/test_gco.py
|
|
162
|
+
tests/test_geometries.py
|
|
161
163
|
tests/test_ifstools.py
|
|
162
164
|
tests/test_import.py
|
|
163
165
|
tests/test_iosponge.py
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from unittest.mock import MagicMock, patch
|
|
2
|
+
import vortex
|
|
3
|
+
from footprints import proxy as fp
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@patch("vortex.tools.systems.OSExtended.ssh")
|
|
7
|
+
@patch("socket.gethostname")
|
|
8
|
+
def test_child_session_setup(mocked_gethostname, mocked_ssh):
|
|
9
|
+
vortex.config.set_config(
|
|
10
|
+
"services", "cluster_names", ["belenos", "taranis"]
|
|
11
|
+
)
|
|
12
|
+
vortex.config.set_config("ecflow", "sshproxy_wait", 12)
|
|
13
|
+
vortex.config.set_config("ecflow", "sshproxy_retries", 4)
|
|
14
|
+
vortex.config.set_config("ecflow", "sshproxy_retrydelay", 2)
|
|
15
|
+
mocked_gethostname.return_value = "belenos191.belenoshpc.meteo.fr"
|
|
16
|
+
s = fp.service(kind="ecflow", clientpath="/path/to/ecflow_client")
|
|
17
|
+
s.env["ECF_HOST"] = "ecfhost"
|
|
18
|
+
s.env["ECF_PORT"] = 28943
|
|
19
|
+
|
|
20
|
+
mocked_ssh.return_value.tunnel.return_value.entranceport = 42
|
|
21
|
+
|
|
22
|
+
with s.child_session_setup() as setup:
|
|
23
|
+
assert setup
|
|
24
|
+
assert s.env["ECF_HOST"] == "localhost"
|
|
25
|
+
assert s.env["ECF_PORT"] == 42
|
|
26
|
+
|
|
27
|
+
mocked_ssh.assert_called_once_with(
|
|
28
|
+
hostname="belenosoper-int",
|
|
29
|
+
mandatory_hostcheck=False,
|
|
30
|
+
maxtries=4,
|
|
31
|
+
triesdelay=2,
|
|
32
|
+
)
|
|
33
|
+
mocked_ssh.return_value.tunnel.assert_called_once_with(
|
|
34
|
+
"ecfhost",
|
|
35
|
+
28943,
|
|
36
|
+
maxwait=12,
|
|
37
|
+
)
|