toil 8.0.0__py3-none-any.whl → 8.2.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.
- toil/__init__.py +4 -39
- toil/batchSystems/abstractBatchSystem.py +1 -1
- toil/batchSystems/abstractGridEngineBatchSystem.py +1 -1
- toil/batchSystems/awsBatch.py +1 -1
- toil/batchSystems/cleanup_support.py +1 -1
- toil/batchSystems/kubernetes.py +53 -7
- toil/batchSystems/local_support.py +1 -1
- toil/batchSystems/mesos/batchSystem.py +13 -8
- toil/batchSystems/mesos/test/__init__.py +3 -2
- toil/batchSystems/options.py +1 -0
- toil/batchSystems/singleMachine.py +1 -1
- toil/batchSystems/slurm.py +229 -84
- toil/bus.py +5 -3
- toil/common.py +198 -54
- toil/cwl/cwltoil.py +32 -11
- toil/job.py +110 -86
- toil/jobStores/abstractJobStore.py +24 -3
- toil/jobStores/aws/jobStore.py +46 -10
- toil/jobStores/fileJobStore.py +25 -1
- toil/jobStores/googleJobStore.py +104 -30
- toil/leader.py +9 -0
- toil/lib/accelerators.py +3 -1
- toil/lib/aws/session.py +14 -3
- toil/lib/aws/utils.py +92 -35
- toil/lib/aws/utils.py.orig +504 -0
- toil/lib/bioio.py +1 -1
- toil/lib/docker.py +252 -91
- toil/lib/dockstore.py +387 -0
- toil/lib/ec2nodes.py +3 -2
- toil/lib/exceptions.py +5 -3
- toil/lib/history.py +1345 -0
- toil/lib/history_submission.py +695 -0
- toil/lib/io.py +56 -23
- toil/lib/misc.py +25 -1
- toil/lib/resources.py +2 -1
- toil/lib/retry.py +10 -10
- toil/lib/threading.py +11 -10
- toil/lib/{integration.py → trs.py} +95 -46
- toil/lib/web.py +38 -0
- toil/options/common.py +25 -2
- toil/options/cwl.py +10 -0
- toil/options/wdl.py +11 -0
- toil/provisioners/gceProvisioner.py +4 -4
- toil/server/api_spec/LICENSE +201 -0
- toil/server/api_spec/README.rst +5 -0
- toil/server/cli/wes_cwl_runner.py +5 -4
- toil/server/utils.py +2 -3
- toil/statsAndLogging.py +35 -1
- toil/test/__init__.py +275 -115
- toil/test/batchSystems/batchSystemTest.py +227 -205
- toil/test/batchSystems/test_slurm.py +199 -2
- toil/test/cactus/pestis.tar.gz +0 -0
- toil/test/conftest.py +7 -0
- toil/test/cwl/2.fasta +11 -0
- toil/test/cwl/2.fastq +12 -0
- toil/test/cwl/conftest.py +39 -0
- toil/test/cwl/cwlTest.py +1015 -780
- toil/test/cwl/directory/directory/file.txt +15 -0
- toil/test/cwl/download_directory_file.json +4 -0
- toil/test/cwl/download_directory_s3.json +4 -0
- toil/test/cwl/download_file.json +6 -0
- toil/test/cwl/download_http.json +6 -0
- toil/test/cwl/download_https.json +6 -0
- toil/test/cwl/download_s3.json +6 -0
- toil/test/cwl/download_subdirectory_file.json +5 -0
- toil/test/cwl/download_subdirectory_s3.json +5 -0
- toil/test/cwl/empty.json +1 -0
- toil/test/cwl/mock_mpi/fake_mpi.yml +8 -0
- toil/test/cwl/mock_mpi/fake_mpi_run.py +42 -0
- toil/test/cwl/optional-file-exists.json +6 -0
- toil/test/cwl/optional-file-missing.json +6 -0
- toil/test/cwl/optional-file.cwl +18 -0
- toil/test/cwl/preemptible_expression.json +1 -0
- toil/test/cwl/revsort-job-missing.json +6 -0
- toil/test/cwl/revsort-job.json +6 -0
- toil/test/cwl/s3_secondary_file.json +16 -0
- toil/test/cwl/seqtk_seq_job.json +6 -0
- toil/test/cwl/stream.json +6 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.dat +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f0 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f1 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f1i +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f2 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f2_TSM0 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f3 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f3_TSM0 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f4 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f4_TSM0 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f5 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.info +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.lock +0 -0
- toil/test/cwl/whale.txt +16 -0
- toil/test/docs/scripts/example_alwaysfail.py +38 -0
- toil/test/docs/scripts/example_alwaysfail_with_files.wdl +33 -0
- toil/test/docs/scripts/example_cachingbenchmark.py +117 -0
- toil/test/docs/scripts/stagingExampleFiles/in.txt +1 -0
- toil/test/docs/scripts/stagingExampleFiles/out.txt +2 -0
- toil/test/docs/scripts/tutorial_arguments.py +23 -0
- toil/test/docs/scripts/tutorial_debugging.patch +12 -0
- toil/test/docs/scripts/tutorial_debugging_hangs.wdl +126 -0
- toil/test/docs/scripts/tutorial_debugging_works.wdl +129 -0
- toil/test/docs/scripts/tutorial_docker.py +20 -0
- toil/test/docs/scripts/tutorial_dynamic.py +24 -0
- toil/test/docs/scripts/tutorial_encapsulation.py +28 -0
- toil/test/docs/scripts/tutorial_encapsulation2.py +29 -0
- toil/test/docs/scripts/tutorial_helloworld.py +15 -0
- toil/test/docs/scripts/tutorial_invokeworkflow.py +27 -0
- toil/test/docs/scripts/tutorial_invokeworkflow2.py +30 -0
- toil/test/docs/scripts/tutorial_jobfunctions.py +22 -0
- toil/test/docs/scripts/tutorial_managing.py +29 -0
- toil/test/docs/scripts/tutorial_managing2.py +56 -0
- toil/test/docs/scripts/tutorial_multiplejobs.py +25 -0
- toil/test/docs/scripts/tutorial_multiplejobs2.py +21 -0
- toil/test/docs/scripts/tutorial_multiplejobs3.py +22 -0
- toil/test/docs/scripts/tutorial_promises.py +25 -0
- toil/test/docs/scripts/tutorial_promises2.py +30 -0
- toil/test/docs/scripts/tutorial_quickstart.py +22 -0
- toil/test/docs/scripts/tutorial_requirements.py +44 -0
- toil/test/docs/scripts/tutorial_services.py +45 -0
- toil/test/docs/scripts/tutorial_staging.py +45 -0
- toil/test/docs/scripts/tutorial_stats.py +64 -0
- toil/test/lib/aws/test_iam.py +3 -1
- toil/test/lib/dockerTest.py +205 -122
- toil/test/lib/test_history.py +236 -0
- toil/test/lib/test_trs.py +161 -0
- toil/test/provisioners/aws/awsProvisionerTest.py +12 -9
- toil/test/provisioners/clusterTest.py +4 -4
- toil/test/provisioners/gceProvisionerTest.py +16 -14
- toil/test/sort/sort.py +4 -1
- toil/test/src/busTest.py +17 -17
- toil/test/src/deferredFunctionTest.py +145 -132
- toil/test/src/importExportFileTest.py +71 -63
- toil/test/src/jobEncapsulationTest.py +27 -28
- toil/test/src/jobServiceTest.py +149 -133
- toil/test/src/jobTest.py +219 -211
- toil/test/src/miscTests.py +66 -60
- toil/test/src/promisedRequirementTest.py +163 -169
- toil/test/src/regularLogTest.py +24 -24
- toil/test/src/resourceTest.py +82 -76
- toil/test/src/restartDAGTest.py +51 -47
- toil/test/src/resumabilityTest.py +24 -19
- toil/test/src/retainTempDirTest.py +60 -57
- toil/test/src/systemTest.py +17 -13
- toil/test/src/threadingTest.py +29 -32
- toil/test/utils/ABCWorkflowDebug/B_file.txt +1 -0
- toil/test/utils/ABCWorkflowDebug/debugWorkflow.py +204 -0
- toil/test/utils/ABCWorkflowDebug/mkFile.py +16 -0
- toil/test/utils/ABCWorkflowDebug/sleep.cwl +12 -0
- toil/test/utils/ABCWorkflowDebug/sleep.yaml +1 -0
- toil/test/utils/toilDebugTest.py +117 -102
- toil/test/utils/toilKillTest.py +54 -53
- toil/test/utils/utilsTest.py +303 -229
- toil/test/wdl/lint_error.wdl +9 -0
- toil/test/wdl/md5sum/empty_file.json +1 -0
- toil/test/wdl/md5sum/md5sum-gs.json +1 -0
- toil/test/wdl/md5sum/md5sum.1.0.wdl +32 -0
- toil/test/wdl/md5sum/md5sum.input +1 -0
- toil/test/wdl/md5sum/md5sum.json +1 -0
- toil/test/wdl/md5sum/md5sum.wdl +25 -0
- toil/test/wdl/miniwdl_self_test/inputs-namespaced.json +1 -0
- toil/test/wdl/miniwdl_self_test/inputs.json +1 -0
- toil/test/wdl/miniwdl_self_test/self_test.wdl +40 -0
- toil/test/wdl/standard_library/as_map.json +16 -0
- toil/test/wdl/standard_library/as_map_as_input.wdl +23 -0
- toil/test/wdl/standard_library/as_pairs.json +7 -0
- toil/test/wdl/standard_library/as_pairs_as_input.wdl +23 -0
- toil/test/wdl/standard_library/ceil.json +3 -0
- toil/test/wdl/standard_library/ceil_as_command.wdl +16 -0
- toil/test/wdl/standard_library/ceil_as_input.wdl +16 -0
- toil/test/wdl/standard_library/collect_by_key.json +1 -0
- toil/test/wdl/standard_library/collect_by_key_as_input.wdl +23 -0
- toil/test/wdl/standard_library/cross.json +11 -0
- toil/test/wdl/standard_library/cross_as_input.wdl +19 -0
- toil/test/wdl/standard_library/flatten.json +7 -0
- toil/test/wdl/standard_library/flatten_as_input.wdl +18 -0
- toil/test/wdl/standard_library/floor.json +3 -0
- toil/test/wdl/standard_library/floor_as_command.wdl +16 -0
- toil/test/wdl/standard_library/floor_as_input.wdl +16 -0
- toil/test/wdl/standard_library/keys.json +8 -0
- toil/test/wdl/standard_library/keys_as_input.wdl +24 -0
- toil/test/wdl/standard_library/length.json +7 -0
- toil/test/wdl/standard_library/length_as_input.wdl +16 -0
- toil/test/wdl/standard_library/length_as_input_with_map.json +7 -0
- toil/test/wdl/standard_library/length_as_input_with_map.wdl +17 -0
- toil/test/wdl/standard_library/length_invalid.json +3 -0
- toil/test/wdl/standard_library/range.json +3 -0
- toil/test/wdl/standard_library/range_0.json +3 -0
- toil/test/wdl/standard_library/range_as_input.wdl +17 -0
- toil/test/wdl/standard_library/range_invalid.json +3 -0
- toil/test/wdl/standard_library/read_boolean.json +3 -0
- toil/test/wdl/standard_library/read_boolean_as_command.wdl +17 -0
- toil/test/wdl/standard_library/read_float.json +3 -0
- toil/test/wdl/standard_library/read_float_as_command.wdl +17 -0
- toil/test/wdl/standard_library/read_int.json +3 -0
- toil/test/wdl/standard_library/read_int_as_command.wdl +17 -0
- toil/test/wdl/standard_library/read_json.json +3 -0
- toil/test/wdl/standard_library/read_json_as_output.wdl +31 -0
- toil/test/wdl/standard_library/read_lines.json +3 -0
- toil/test/wdl/standard_library/read_lines_as_output.wdl +31 -0
- toil/test/wdl/standard_library/read_map.json +3 -0
- toil/test/wdl/standard_library/read_map_as_output.wdl +31 -0
- toil/test/wdl/standard_library/read_string.json +3 -0
- toil/test/wdl/standard_library/read_string_as_command.wdl +17 -0
- toil/test/wdl/standard_library/read_tsv.json +3 -0
- toil/test/wdl/standard_library/read_tsv_as_output.wdl +31 -0
- toil/test/wdl/standard_library/round.json +3 -0
- toil/test/wdl/standard_library/round_as_command.wdl +16 -0
- toil/test/wdl/standard_library/round_as_input.wdl +16 -0
- toil/test/wdl/standard_library/size.json +3 -0
- toil/test/wdl/standard_library/size_as_command.wdl +17 -0
- toil/test/wdl/standard_library/size_as_output.wdl +36 -0
- toil/test/wdl/standard_library/stderr.json +3 -0
- toil/test/wdl/standard_library/stderr_as_output.wdl +30 -0
- toil/test/wdl/standard_library/stdout.json +3 -0
- toil/test/wdl/standard_library/stdout_as_output.wdl +30 -0
- toil/test/wdl/standard_library/sub.json +3 -0
- toil/test/wdl/standard_library/sub_as_input.wdl +17 -0
- toil/test/wdl/standard_library/sub_as_input_with_file.wdl +17 -0
- toil/test/wdl/standard_library/transpose.json +6 -0
- toil/test/wdl/standard_library/transpose_as_input.wdl +18 -0
- toil/test/wdl/standard_library/write_json.json +6 -0
- toil/test/wdl/standard_library/write_json_as_command.wdl +17 -0
- toil/test/wdl/standard_library/write_lines.json +7 -0
- toil/test/wdl/standard_library/write_lines_as_command.wdl +17 -0
- toil/test/wdl/standard_library/write_map.json +6 -0
- toil/test/wdl/standard_library/write_map_as_command.wdl +17 -0
- toil/test/wdl/standard_library/write_tsv.json +6 -0
- toil/test/wdl/standard_library/write_tsv_as_command.wdl +17 -0
- toil/test/wdl/standard_library/zip.json +12 -0
- toil/test/wdl/standard_library/zip_as_input.wdl +19 -0
- toil/test/wdl/test.csv +3 -0
- toil/test/wdl/test.tsv +3 -0
- toil/test/wdl/testfiles/croo.wdl +38 -0
- toil/test/wdl/testfiles/drop_files.wdl +62 -0
- toil/test/wdl/testfiles/drop_files_subworkflow.wdl +13 -0
- toil/test/wdl/testfiles/empty.txt +0 -0
- toil/test/wdl/testfiles/not_enough_outputs.wdl +33 -0
- toil/test/wdl/testfiles/random.wdl +66 -0
- toil/test/wdl/testfiles/string_file_coercion.json +1 -0
- toil/test/wdl/testfiles/string_file_coercion.wdl +35 -0
- toil/test/wdl/testfiles/test.json +4 -0
- toil/test/wdl/testfiles/test_boolean.txt +1 -0
- toil/test/wdl/testfiles/test_float.txt +1 -0
- toil/test/wdl/testfiles/test_int.txt +1 -0
- toil/test/wdl/testfiles/test_lines.txt +5 -0
- toil/test/wdl/testfiles/test_map.txt +2 -0
- toil/test/wdl/testfiles/test_string.txt +1 -0
- toil/test/wdl/testfiles/url_to_file.wdl +13 -0
- toil/test/wdl/testfiles/url_to_optional_file.wdl +13 -0
- toil/test/wdl/testfiles/vocab.json +1 -0
- toil/test/wdl/testfiles/vocab.wdl +66 -0
- toil/test/wdl/testfiles/wait.wdl +34 -0
- toil/test/wdl/wdl_specification/type_pair.json +23 -0
- toil/test/wdl/wdl_specification/type_pair_basic.wdl +36 -0
- toil/test/wdl/wdl_specification/type_pair_with_files.wdl +36 -0
- toil/test/wdl/wdl_specification/v1_spec.json +1 -0
- toil/test/wdl/wdl_specification/v1_spec_declaration.wdl +39 -0
- toil/test/wdl/wdltoil_test.py +681 -408
- toil/test/wdl/wdltoil_test_kubernetes.py +2 -2
- toil/version.py +10 -10
- toil/wdl/wdltoil.py +350 -123
- toil/worker.py +113 -33
- {toil-8.0.0.dist-info → toil-8.2.0.dist-info}/METADATA +13 -7
- toil-8.2.0.dist-info/RECORD +439 -0
- {toil-8.0.0.dist-info → toil-8.2.0.dist-info}/WHEEL +1 -1
- toil/test/lib/test_integration.py +0 -104
- toil-8.0.0.dist-info/RECORD +0 -253
- {toil-8.0.0.dist-info → toil-8.2.0.dist-info}/entry_points.txt +0 -0
- {toil-8.0.0.dist-info → toil-8.2.0.dist-info/licenses}/LICENSE +0 -0
- {toil-8.0.0.dist-info → toil-8.2.0.dist-info}/top_level.txt +0 -0
toil/lib/dockstore.py
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
# Copyright (C) 2024 Regents of the University of California
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Contains functions for integrating Toil with UCSC Dockstore, for reporting metrics.
|
|
17
|
+
|
|
18
|
+
For basic TRS functionality for fetching workflows, see trs.py.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import datetime
|
|
22
|
+
import logging
|
|
23
|
+
import math
|
|
24
|
+
import os
|
|
25
|
+
import re
|
|
26
|
+
import sys
|
|
27
|
+
import uuid
|
|
28
|
+
from typing import Any, Literal, Optional, Union, TypedDict, cast
|
|
29
|
+
|
|
30
|
+
from urllib.parse import urlparse, unquote, quote
|
|
31
|
+
import requests
|
|
32
|
+
|
|
33
|
+
from toil.lib.misc import unix_seconds_to_timestamp, seconds_to_duration
|
|
34
|
+
from toil.lib.trs import TRS_ROOT
|
|
35
|
+
from toil.lib.retry import retry
|
|
36
|
+
from toil.lib.web import web_session
|
|
37
|
+
from toil.version import baseVersion
|
|
38
|
+
|
|
39
|
+
if sys.version_info < (3, 11):
|
|
40
|
+
from typing_extensions import NotRequired
|
|
41
|
+
else:
|
|
42
|
+
from typing import NotRequired
|
|
43
|
+
|
|
44
|
+
logger = logging.getLogger(__name__)
|
|
45
|
+
|
|
46
|
+
# We assume TRS_ROOT is actually a Dockstore instance.
|
|
47
|
+
|
|
48
|
+
# This is a publish-able token for production Dockstore for Toil to use.
|
|
49
|
+
# This is NOT a secret value.
|
|
50
|
+
DEFAULT_DOCKSTORE_TOKEN = "2bff46294daddef6df185452b04db6143ea8a59f52ee3c325d3e1df418511b7d"
|
|
51
|
+
|
|
52
|
+
# How should we authenticate our Dockstore requests?
|
|
53
|
+
DOCKSTORE_TOKEN = os.environ.get("TOIL_DOCKSTORE_TOKEN", DEFAULT_DOCKSTORE_TOKEN)
|
|
54
|
+
|
|
55
|
+
# What platform should we report metrics as?
|
|
56
|
+
DOCKSTORE_PLATFORM = "TOIL"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# This is a https://schema.org/CompletedActionStatus
|
|
60
|
+
# The values here are from expanding the type info in the Docksotre docs at
|
|
61
|
+
# <https://dockstore.org/api/static/swagger-ui/index.html#/extendedGA4GH/executionMetricsPost>
|
|
62
|
+
ExecutionStatus = Union[Literal["ALL"], Literal["SUCCESSFUL"], Literal["FAILED"], Literal["FAILED_SEMANTIC_INVALID"], Literal["FAILED_RUNTIME_INVALID"], Literal["ABORTED"]]
|
|
63
|
+
|
|
64
|
+
class Cost(TypedDict):
|
|
65
|
+
"""
|
|
66
|
+
Representation of the cost of running something.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
value: float
|
|
70
|
+
"""
|
|
71
|
+
Cost in US Dollars.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
class RunExecution(TypedDict):
|
|
75
|
+
"""
|
|
76
|
+
Dockstore metrics data for a workflow or task run.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
executionId: str
|
|
80
|
+
"""
|
|
81
|
+
Executor-generated unique execution ID for this workflow or task.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
dateExecuted: str
|
|
85
|
+
"""
|
|
86
|
+
ISO 8601 UTC timestamp when the execution happend.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
executionStatus: ExecutionStatus
|
|
90
|
+
"""
|
|
91
|
+
Did the execution work?
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
executionTime: NotRequired[str]
|
|
95
|
+
"""
|
|
96
|
+
Total time of the run in ISO 8601 duration format.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
# TODO: Is this meant to be actual usage or amount provided?
|
|
100
|
+
memoryRequirementsGB: NotRequired[float]
|
|
101
|
+
"""
|
|
102
|
+
Memory required for the execution in gigabytes (not GiB).
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
cpuRequirements: NotRequired[int]
|
|
106
|
+
"""
|
|
107
|
+
Number of CPUs required.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
cost: NotRequired[Cost]
|
|
111
|
+
"""
|
|
112
|
+
How much the execution cost to run.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
# TODO: What if two cloud providers have the same region naming scheme?
|
|
116
|
+
region: NotRequired[str]
|
|
117
|
+
"""
|
|
118
|
+
The (cloud) region the workflow was executed in.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
additionalProperties: NotRequired[dict[str, Any]]
|
|
122
|
+
"""
|
|
123
|
+
Any additional properties to send.
|
|
124
|
+
|
|
125
|
+
Dockstore can take any JSON-able structured data.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
class TaskExecutions(TypedDict):
|
|
129
|
+
"""
|
|
130
|
+
Dockstore metrics data for all the tasks in a workflow.
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
# TODO: Right now we use different IDs for the workflow RunExecution and
|
|
134
|
+
# for its corresponding collection of TaskExecutions, so there's no nice
|
|
135
|
+
# way to find the one from the other.
|
|
136
|
+
executionId: str
|
|
137
|
+
"""
|
|
138
|
+
Executor-generated unique execution ID.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
dateExecuted: str
|
|
142
|
+
"""
|
|
143
|
+
ISO 8601 UTC timestamp when the execution happend.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
taskExecutions: list[RunExecution]
|
|
147
|
+
"""
|
|
148
|
+
Individual executions of each task in the workflow.
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
additionalProperties: NotRequired[dict[str, Any]]
|
|
152
|
+
"""
|
|
153
|
+
Any additional properties to send.
|
|
154
|
+
|
|
155
|
+
Dockstore can take any JSON-able structured data.
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
def ensure_valid_id(execution_id: str) -> None:
|
|
159
|
+
"""
|
|
160
|
+
Make sure the given execution ID is in Dockstore format and will be accepted by Dockstore.
|
|
161
|
+
|
|
162
|
+
Must be alphanumeric (with internal underscores allowed) and <100
|
|
163
|
+
characters long.
|
|
164
|
+
|
|
165
|
+
:raises ValueError: if the ID is not in the right format
|
|
166
|
+
"""
|
|
167
|
+
if len(execution_id) >= 100:
|
|
168
|
+
raise ValueError("Execution ID too long")
|
|
169
|
+
if len(execution_id) == 0:
|
|
170
|
+
raise ValueError("Execution ID must not be empty")
|
|
171
|
+
if execution_id[0] == "_" or execution_id[-1] == "_":
|
|
172
|
+
raise ValueError("Execution ID must not start or end with an underscore")
|
|
173
|
+
if not re.fullmatch("[a-zA-Z0-9_]+", execution_id):
|
|
174
|
+
raise ValueError("Execution ID must be alphanumeric with internal underscores")
|
|
175
|
+
|
|
176
|
+
def pack_workflow_metrics(
|
|
177
|
+
execution_id: str,
|
|
178
|
+
start_time: float,
|
|
179
|
+
runtime: float,
|
|
180
|
+
succeeded: bool,
|
|
181
|
+
job_store_type: Optional[str] = None,
|
|
182
|
+
batch_system: Optional[str] = None,
|
|
183
|
+
caching: Optional[bool] = None,
|
|
184
|
+
toil_version: Optional[str] = None,
|
|
185
|
+
python_version: Optional[str] = None,
|
|
186
|
+
platform_system: Optional[str] = None,
|
|
187
|
+
platform_machine: Optional[str] = None
|
|
188
|
+
) -> RunExecution:
|
|
189
|
+
"""
|
|
190
|
+
Pack up per-workflow metrics into a format that can be submitted to Dockstore.
|
|
191
|
+
|
|
192
|
+
:param execution_id: Unique ID for the workflow execution. Must be in
|
|
193
|
+
Dockstore format.
|
|
194
|
+
:param start_time: Execution start time in seconds since the Unix epoch.
|
|
195
|
+
:param rutime: Execution duration in seconds.
|
|
196
|
+
:param jobstore_type: Kind of job store used, like "file" or "aws".
|
|
197
|
+
:param batch_system: Python class name implementing the batch system used.
|
|
198
|
+
:param caching: Whether Toil filestore-level cahcing was used.
|
|
199
|
+
:param toil_version: Version of Toil used (without any Git hash).
|
|
200
|
+
:param python_version: Version of Python used.
|
|
201
|
+
:param platform_system: Operating system type (like "Darwin" or "Linux").
|
|
202
|
+
:param platform_machine: Machine architecture of the leader (like "AMD64").
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
# Enforce Dockstore's constraints
|
|
206
|
+
ensure_valid_id(execution_id)
|
|
207
|
+
|
|
208
|
+
# Pack up into a RunExecution
|
|
209
|
+
result = RunExecution(
|
|
210
|
+
executionId=execution_id,
|
|
211
|
+
dateExecuted=unix_seconds_to_timestamp(start_time),
|
|
212
|
+
executionTime=seconds_to_duration(runtime),
|
|
213
|
+
executionStatus="SUCCESSFUL" if succeeded else "FAILED"
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# TODO: Just use kwargs here?
|
|
217
|
+
additional_properties: dict[str, Any] = {}
|
|
218
|
+
|
|
219
|
+
if job_store_type is not None:
|
|
220
|
+
additional_properties["jobStoreType"] = job_store_type
|
|
221
|
+
|
|
222
|
+
if batch_system is not None:
|
|
223
|
+
additional_properties["batchSystem"] = batch_system
|
|
224
|
+
|
|
225
|
+
if caching is not None:
|
|
226
|
+
additional_properties["caching"] = caching
|
|
227
|
+
|
|
228
|
+
if toil_version is not None:
|
|
229
|
+
additional_properties["toilVersion"] = toil_version
|
|
230
|
+
|
|
231
|
+
if python_version is not None:
|
|
232
|
+
additional_properties["pythonVersion"] = python_version
|
|
233
|
+
|
|
234
|
+
if platform_system is not None:
|
|
235
|
+
additional_properties["platformSystem"] = platform_system
|
|
236
|
+
|
|
237
|
+
if platform_machine is not None:
|
|
238
|
+
additional_properties["platformMachine"] = platform_machine
|
|
239
|
+
|
|
240
|
+
if len(additional_properties) > 0:
|
|
241
|
+
result["additionalProperties"] = additional_properties
|
|
242
|
+
|
|
243
|
+
return result
|
|
244
|
+
|
|
245
|
+
def pack_single_task_metrics(
|
|
246
|
+
execution_id: str,
|
|
247
|
+
start_time: float,
|
|
248
|
+
runtime: float,
|
|
249
|
+
succeeded: bool,
|
|
250
|
+
job_name: Optional[str] = None,
|
|
251
|
+
cores: Optional[float] = None,
|
|
252
|
+
cpu_seconds: Optional[float] = None,
|
|
253
|
+
memory_bytes: Optional[int] = None,
|
|
254
|
+
disk_bytes: Optional[int] = None,
|
|
255
|
+
) -> RunExecution:
|
|
256
|
+
"""
|
|
257
|
+
Pack up metrics for a single task execution in a format that can be used in a Dockstore submission.
|
|
258
|
+
|
|
259
|
+
:param execution_id: Unique ID for the workflow execution. Must be in
|
|
260
|
+
Dockstore format.
|
|
261
|
+
:param start_time: Execution start time in seconds since the Unix epoch.
|
|
262
|
+
:param rutime: Execution duration in seconds.
|
|
263
|
+
:param succeeded: Whether the execution succeeded.
|
|
264
|
+
:param job_name: Name of the job within the workflow.
|
|
265
|
+
:param cores: CPU cores allocated to the job.
|
|
266
|
+
:param cpu_seconds: CPU seconds consumed by the job.
|
|
267
|
+
:param memory_bytes: Memory consumed by the job in bytes.
|
|
268
|
+
:param disk_bytes: Disk space consumed by the job in bytes.
|
|
269
|
+
"""
|
|
270
|
+
|
|
271
|
+
# TODO: Deduplicate with workflow code since the output type is the same.
|
|
272
|
+
|
|
273
|
+
# Enforce Dockstore's constraints
|
|
274
|
+
ensure_valid_id(execution_id)
|
|
275
|
+
|
|
276
|
+
# Pack up into a RunExecution
|
|
277
|
+
result = RunExecution(
|
|
278
|
+
executionId=execution_id,
|
|
279
|
+
dateExecuted=unix_seconds_to_timestamp(start_time),
|
|
280
|
+
executionTime=seconds_to_duration(runtime),
|
|
281
|
+
executionStatus="SUCCESSFUL" if succeeded else "FAILED"
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
if memory_bytes is not None:
|
|
285
|
+
# Convert bytes to fractional gigabytes
|
|
286
|
+
result["memoryRequirementsGB"] = memory_bytes / 1_000_000_000
|
|
287
|
+
|
|
288
|
+
if cores is not None:
|
|
289
|
+
# Convert possibly fractional cores to an integer for Dockstore
|
|
290
|
+
result["cpuRequirements"] = int(math.ceil(cores))
|
|
291
|
+
|
|
292
|
+
# TODO: Just use kwargs here?
|
|
293
|
+
additional_properties: dict[str, Any] = {}
|
|
294
|
+
|
|
295
|
+
if job_name is not None:
|
|
296
|
+
# Convert to Doskstore-style camelCase property keys
|
|
297
|
+
additional_properties["jobName"] = job_name
|
|
298
|
+
|
|
299
|
+
if disk_bytes is not None:
|
|
300
|
+
# Convert to a Dockstore-style fractional disk gigabytes
|
|
301
|
+
additional_properties["diskRequirementsGB"] = disk_bytes / 1_000_000_000
|
|
302
|
+
|
|
303
|
+
if cpu_seconds is not None:
|
|
304
|
+
# Use a Dockstore-ier name here too
|
|
305
|
+
additional_properties["cpuRequirementsCoreSeconds"] = cpu_seconds
|
|
306
|
+
|
|
307
|
+
if len(additional_properties) > 0:
|
|
308
|
+
result["additionalProperties"] = additional_properties
|
|
309
|
+
|
|
310
|
+
return result
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def pack_workflow_task_set_metrics(execution_id: str, start_time: float, tasks: list[RunExecution]) -> TaskExecutions:
|
|
314
|
+
"""
|
|
315
|
+
Pack up metrics for all the tasks in a workflow execution into a format that can be submitted to Dockstore.
|
|
316
|
+
|
|
317
|
+
:param execution_id: Unique ID for the workflow execution. Must be in
|
|
318
|
+
Dockstore format.
|
|
319
|
+
:param start_time: Execution start time for the overall workflow execution
|
|
320
|
+
in seconds since the Unix epoch.
|
|
321
|
+
:param tasks: Packed tasks from pack_single_task_metrics()
|
|
322
|
+
"""
|
|
323
|
+
|
|
324
|
+
# Enforce Dockstore's constraints
|
|
325
|
+
ensure_valid_id(execution_id)
|
|
326
|
+
|
|
327
|
+
return TaskExecutions(
|
|
328
|
+
executionId=execution_id,
|
|
329
|
+
dateExecuted=unix_seconds_to_timestamp(start_time),
|
|
330
|
+
taskExecutions=tasks
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
def send_metrics(trs_workflow_id: str, trs_version: str, workflow_runs: list[RunExecution], workflow_task_sets: list[TaskExecutions]) -> None:
|
|
334
|
+
"""
|
|
335
|
+
Send packed workflow and/or task metrics to Dockstore.
|
|
336
|
+
|
|
337
|
+
:param workflow_runs: list of packed metrics objects for each workflow.
|
|
338
|
+
|
|
339
|
+
:param workflow_task_sets: list of packed metrics objects for the tasks in
|
|
340
|
+
each workflow. Each workflow should have one entry containing all its
|
|
341
|
+
tasks. Does not have to be the same order/set of workflows as
|
|
342
|
+
workflow_runs.
|
|
343
|
+
|
|
344
|
+
:raises requests.HTTPError: if Dockstore does not accept the metrics.
|
|
345
|
+
"""
|
|
346
|
+
|
|
347
|
+
# Aggregate into a submission
|
|
348
|
+
to_post = {
|
|
349
|
+
"runExecutions": workflow_runs,
|
|
350
|
+
"taskExecutions": workflow_task_sets,
|
|
351
|
+
"validationExecutions": []
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
# Set the submission query string metadata
|
|
355
|
+
submission_params = {
|
|
356
|
+
"platform": DOCKSTORE_PLATFORM,
|
|
357
|
+
"description": "Workflow status from Toil"
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
# Set the headers. Even though user agent isn't in here, it still gets
|
|
361
|
+
# sent.
|
|
362
|
+
headers = {}
|
|
363
|
+
if DOCKSTORE_TOKEN is not None:
|
|
364
|
+
headers["Authorization"] = f"Bearer {DOCKSTORE_TOKEN}"
|
|
365
|
+
|
|
366
|
+
# Note that Dockstore's metrics apparently need two levels of /api for some reason.
|
|
367
|
+
endpoint_url = f"{TRS_ROOT}/api/api/ga4gh/v2/extended/{quote(trs_workflow_id, safe='')}/versions/{quote(trs_version, safe='')}/executions"
|
|
368
|
+
|
|
369
|
+
logger.info("Sending workflow metrics to %s", endpoint_url)
|
|
370
|
+
logger.debug("With data: %s", to_post)
|
|
371
|
+
logger.debug("With headers: %s", headers)
|
|
372
|
+
|
|
373
|
+
try:
|
|
374
|
+
result = web_session.post(endpoint_url, params=submission_params, json=to_post, headers=headers)
|
|
375
|
+
result.raise_for_status()
|
|
376
|
+
logger.debug("Workflow metrics were accepted by Dockstore. Dockstore response code: %s", result.status_code)
|
|
377
|
+
except requests.HTTPError as e:
|
|
378
|
+
logger.warning("Workflow metrics were not accepted by Dockstore. Dockstore complained: %s", e.response.text)
|
|
379
|
+
raise
|
|
380
|
+
|
|
381
|
+
def get_metrics_url(trs_workflow_id: str, trs_version: str, execution_id: str) -> str:
|
|
382
|
+
"""
|
|
383
|
+
Get the URL where a workflow metrics object (for a workflow, or for a set of tasks) can be fetched back from.
|
|
384
|
+
"""
|
|
385
|
+
|
|
386
|
+
return f"{TRS_ROOT}/api/api/ga4gh/v2/extended/{quote(trs_workflow_id, safe='')}/versions/{quote(trs_version, safe='')}/execution?platform={DOCKSTORE_PLATFORM}&executionId={quote(execution_id, safe='')}"
|
|
387
|
+
|
toil/lib/ec2nodes.py
CHANGED
|
@@ -21,7 +21,8 @@ import textwrap
|
|
|
21
21
|
from typing import Any, Union
|
|
22
22
|
|
|
23
23
|
import enlighten # type: ignore
|
|
24
|
-
|
|
24
|
+
|
|
25
|
+
from toil.lib.web import web_session
|
|
25
26
|
|
|
26
27
|
logger = logging.getLogger(__name__)
|
|
27
28
|
manager = enlighten.get_manager()
|
|
@@ -193,7 +194,7 @@ def download_region_json(filename: str, region: str = "us-east-1") -> None:
|
|
|
193
194
|
aws instance name (example: 't2.micro'), and the value is an
|
|
194
195
|
InstanceType object representing that aws instance name.
|
|
195
196
|
"""
|
|
196
|
-
response =
|
|
197
|
+
response = web_session.get(
|
|
197
198
|
f"https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonEC2/current/{region}/index.json",
|
|
198
199
|
stream=True,
|
|
199
200
|
)
|
toil/lib/exceptions.py
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
# 5.14.2018: copied into Toil from https://github.com/BD2KGenomics/bd2k-python-lib
|
|
16
16
|
|
|
17
17
|
import sys
|
|
18
|
+
from typing import Optional
|
|
19
|
+
import logging
|
|
18
20
|
from urllib.parse import ParseResult
|
|
19
21
|
|
|
20
22
|
|
|
@@ -39,15 +41,15 @@ class panic:
|
|
|
39
41
|
the primary exception will be reraised.
|
|
40
42
|
"""
|
|
41
43
|
|
|
42
|
-
def __init__(self, log=None):
|
|
44
|
+
def __init__(self, log: Optional[logging.Logger] = None) -> None:
|
|
43
45
|
super().__init__()
|
|
44
46
|
self.log = log
|
|
45
47
|
self.exc_info = None
|
|
46
48
|
|
|
47
|
-
def __enter__(self):
|
|
49
|
+
def __enter__(self) -> None:
|
|
48
50
|
self.exc_info = sys.exc_info()
|
|
49
51
|
|
|
50
|
-
def __exit__(self, *exc_info):
|
|
52
|
+
def __exit__(self, *exc_info) -> None:
|
|
51
53
|
if self.log is not None and exc_info and exc_info[0]:
|
|
52
54
|
self.log.warning("Exception during panic", exc_info=exc_info)
|
|
53
55
|
exc_type, exc_value, traceback = self.exc_info
|