toil 6.1.0a1__py3-none-any.whl → 8.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.
- toil/__init__.py +122 -315
- toil/batchSystems/__init__.py +1 -0
- toil/batchSystems/abstractBatchSystem.py +173 -89
- toil/batchSystems/abstractGridEngineBatchSystem.py +272 -148
- toil/batchSystems/awsBatch.py +244 -135
- toil/batchSystems/cleanup_support.py +26 -16
- toil/batchSystems/contained_executor.py +31 -28
- toil/batchSystems/gridengine.py +86 -50
- toil/batchSystems/htcondor.py +166 -89
- toil/batchSystems/kubernetes.py +632 -382
- toil/batchSystems/local_support.py +20 -15
- toil/batchSystems/lsf.py +134 -81
- toil/batchSystems/lsfHelper.py +13 -11
- toil/batchSystems/mesos/__init__.py +41 -29
- toil/batchSystems/mesos/batchSystem.py +290 -151
- toil/batchSystems/mesos/executor.py +79 -50
- toil/batchSystems/mesos/test/__init__.py +31 -23
- toil/batchSystems/options.py +46 -28
- toil/batchSystems/registry.py +53 -19
- toil/batchSystems/singleMachine.py +296 -125
- toil/batchSystems/slurm.py +603 -138
- toil/batchSystems/torque.py +47 -33
- toil/bus.py +186 -76
- toil/common.py +664 -368
- toil/cwl/__init__.py +1 -1
- toil/cwl/cwltoil.py +1136 -483
- toil/cwl/utils.py +17 -22
- toil/deferred.py +63 -42
- toil/exceptions.py +5 -3
- toil/fileStores/__init__.py +5 -5
- toil/fileStores/abstractFileStore.py +140 -60
- toil/fileStores/cachingFileStore.py +717 -269
- toil/fileStores/nonCachingFileStore.py +116 -87
- toil/job.py +1225 -368
- toil/jobStores/abstractJobStore.py +416 -266
- toil/jobStores/aws/jobStore.py +863 -477
- toil/jobStores/aws/utils.py +201 -120
- toil/jobStores/conftest.py +3 -2
- toil/jobStores/fileJobStore.py +292 -154
- toil/jobStores/googleJobStore.py +140 -74
- toil/jobStores/utils.py +36 -15
- toil/leader.py +668 -272
- toil/lib/accelerators.py +115 -18
- toil/lib/aws/__init__.py +74 -31
- toil/lib/aws/ami.py +122 -87
- toil/lib/aws/iam.py +284 -108
- toil/lib/aws/s3.py +31 -0
- toil/lib/aws/session.py +214 -39
- toil/lib/aws/utils.py +287 -231
- toil/lib/bioio.py +13 -5
- toil/lib/compatibility.py +11 -6
- toil/lib/conversions.py +104 -47
- toil/lib/docker.py +131 -103
- toil/lib/ec2.py +361 -199
- toil/lib/ec2nodes.py +174 -106
- toil/lib/encryption/_dummy.py +5 -3
- toil/lib/encryption/_nacl.py +10 -6
- toil/lib/encryption/conftest.py +1 -0
- toil/lib/exceptions.py +26 -7
- toil/lib/expando.py +5 -3
- toil/lib/ftp_utils.py +217 -0
- toil/lib/generatedEC2Lists.py +127 -19
- toil/lib/humanize.py +6 -2
- toil/lib/integration.py +341 -0
- toil/lib/io.py +141 -15
- toil/lib/iterables.py +4 -2
- toil/lib/memoize.py +12 -8
- toil/lib/misc.py +66 -21
- toil/lib/objects.py +2 -2
- toil/lib/resources.py +68 -15
- toil/lib/retry.py +126 -81
- toil/lib/threading.py +299 -82
- toil/lib/throttle.py +16 -15
- toil/options/common.py +843 -409
- toil/options/cwl.py +175 -90
- toil/options/runner.py +50 -0
- toil/options/wdl.py +73 -17
- toil/provisioners/__init__.py +117 -46
- toil/provisioners/abstractProvisioner.py +332 -157
- toil/provisioners/aws/__init__.py +70 -33
- toil/provisioners/aws/awsProvisioner.py +1145 -715
- toil/provisioners/clusterScaler.py +541 -279
- toil/provisioners/gceProvisioner.py +282 -179
- toil/provisioners/node.py +155 -79
- toil/realtimeLogger.py +34 -22
- toil/resource.py +137 -75
- toil/server/app.py +128 -62
- toil/server/celery_app.py +3 -1
- toil/server/cli/wes_cwl_runner.py +82 -53
- toil/server/utils.py +54 -28
- toil/server/wes/abstract_backend.py +64 -26
- toil/server/wes/amazon_wes_utils.py +21 -15
- toil/server/wes/tasks.py +121 -63
- toil/server/wes/toil_backend.py +142 -107
- toil/server/wsgi_app.py +4 -3
- toil/serviceManager.py +58 -22
- toil/statsAndLogging.py +224 -70
- toil/test/__init__.py +282 -183
- toil/test/batchSystems/batchSystemTest.py +460 -210
- toil/test/batchSystems/batch_system_plugin_test.py +90 -0
- toil/test/batchSystems/test_gridengine.py +173 -0
- toil/test/batchSystems/test_lsf_helper.py +67 -58
- toil/test/batchSystems/test_slurm.py +110 -49
- toil/test/cactus/__init__.py +0 -0
- toil/test/cactus/test_cactus_integration.py +56 -0
- toil/test/cwl/cwlTest.py +496 -287
- toil/test/cwl/measure_default_memory.cwl +12 -0
- toil/test/cwl/not_run_required_input.cwl +29 -0
- toil/test/cwl/scatter_duplicate_outputs.cwl +40 -0
- toil/test/cwl/seqtk_seq.cwl +1 -1
- toil/test/docs/scriptsTest.py +69 -46
- toil/test/jobStores/jobStoreTest.py +427 -264
- toil/test/lib/aws/test_iam.py +118 -50
- toil/test/lib/aws/test_s3.py +16 -9
- toil/test/lib/aws/test_utils.py +5 -6
- toil/test/lib/dockerTest.py +118 -141
- toil/test/lib/test_conversions.py +113 -115
- toil/test/lib/test_ec2.py +58 -50
- toil/test/lib/test_integration.py +104 -0
- toil/test/lib/test_misc.py +12 -5
- toil/test/mesos/MesosDataStructuresTest.py +23 -10
- toil/test/mesos/helloWorld.py +7 -6
- toil/test/mesos/stress.py +25 -20
- toil/test/options/__init__.py +13 -0
- toil/test/options/options.py +42 -0
- toil/test/provisioners/aws/awsProvisionerTest.py +320 -150
- toil/test/provisioners/clusterScalerTest.py +440 -250
- toil/test/provisioners/clusterTest.py +166 -44
- toil/test/provisioners/gceProvisionerTest.py +174 -100
- toil/test/provisioners/provisionerTest.py +25 -13
- toil/test/provisioners/restartScript.py +5 -4
- toil/test/server/serverTest.py +188 -141
- toil/test/sort/restart_sort.py +137 -68
- toil/test/sort/sort.py +134 -66
- toil/test/sort/sortTest.py +91 -49
- toil/test/src/autoDeploymentTest.py +141 -101
- toil/test/src/busTest.py +20 -18
- toil/test/src/checkpointTest.py +8 -2
- toil/test/src/deferredFunctionTest.py +49 -35
- toil/test/src/dockerCheckTest.py +32 -24
- toil/test/src/environmentTest.py +135 -0
- toil/test/src/fileStoreTest.py +539 -272
- toil/test/src/helloWorldTest.py +7 -4
- toil/test/src/importExportFileTest.py +61 -31
- toil/test/src/jobDescriptionTest.py +46 -21
- toil/test/src/jobEncapsulationTest.py +2 -0
- toil/test/src/jobFileStoreTest.py +74 -50
- toil/test/src/jobServiceTest.py +187 -73
- toil/test/src/jobTest.py +121 -71
- toil/test/src/miscTests.py +19 -18
- toil/test/src/promisedRequirementTest.py +82 -36
- toil/test/src/promisesTest.py +7 -6
- toil/test/src/realtimeLoggerTest.py +10 -6
- toil/test/src/regularLogTest.py +71 -37
- toil/test/src/resourceTest.py +80 -49
- toil/test/src/restartDAGTest.py +36 -22
- toil/test/src/resumabilityTest.py +9 -2
- toil/test/src/retainTempDirTest.py +45 -14
- toil/test/src/systemTest.py +12 -8
- toil/test/src/threadingTest.py +44 -25
- toil/test/src/toilContextManagerTest.py +10 -7
- toil/test/src/userDefinedJobArgTypeTest.py +8 -5
- toil/test/src/workerTest.py +73 -23
- toil/test/utils/toilDebugTest.py +103 -33
- toil/test/utils/toilKillTest.py +4 -5
- toil/test/utils/utilsTest.py +245 -106
- toil/test/wdl/wdltoil_test.py +818 -149
- toil/test/wdl/wdltoil_test_kubernetes.py +91 -0
- toil/toilState.py +120 -35
- toil/utils/toilConfig.py +13 -4
- toil/utils/toilDebugFile.py +44 -27
- toil/utils/toilDebugJob.py +214 -27
- toil/utils/toilDestroyCluster.py +11 -6
- toil/utils/toilKill.py +8 -3
- toil/utils/toilLaunchCluster.py +256 -140
- toil/utils/toilMain.py +37 -16
- toil/utils/toilRsyncCluster.py +32 -14
- toil/utils/toilSshCluster.py +49 -22
- toil/utils/toilStats.py +356 -273
- toil/utils/toilStatus.py +292 -139
- toil/utils/toilUpdateEC2Instances.py +3 -1
- toil/version.py +12 -12
- toil/wdl/utils.py +5 -5
- toil/wdl/wdltoil.py +3913 -1033
- toil/worker.py +367 -184
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/LICENSE +25 -0
- toil-8.0.0.dist-info/METADATA +173 -0
- toil-8.0.0.dist-info/RECORD +253 -0
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/WHEEL +1 -1
- toil-6.1.0a1.dist-info/METADATA +0 -125
- toil-6.1.0a1.dist-info/RECORD +0 -237
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/entry_points.txt +0 -0
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/top_level.txt +0 -0
toil/utils/toilLaunchCluster.py
CHANGED
|
@@ -15,106 +15,189 @@
|
|
|
15
15
|
|
|
16
16
|
import logging
|
|
17
17
|
import os
|
|
18
|
-
from typing import
|
|
18
|
+
from typing import Union
|
|
19
19
|
|
|
20
20
|
from toil import applianceSelf
|
|
21
21
|
from toil.common import parser_with_common_options
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
from toil.lib.aws import build_tag_dict_from_env
|
|
25
|
+
except ModuleNotFoundError:
|
|
26
|
+
build_tag_dict_from_env: dict[str, str] = lambda _: {} # type: ignore[no-redef]
|
|
27
|
+
from toil.lib.conversions import opt_strtobool
|
|
28
|
+
from toil.provisioners import check_valid_node_types, cluster_factory, parse_node_types
|
|
26
29
|
from toil.statsAndLogging import set_logging_from_options
|
|
27
30
|
|
|
28
31
|
logger = logging.getLogger(__name__)
|
|
29
32
|
|
|
30
33
|
|
|
31
|
-
def create_tags_dict(tags:
|
|
34
|
+
def create_tags_dict(tags: list[str]) -> dict[str, str]:
|
|
32
35
|
tags_dict = dict()
|
|
33
36
|
for tag in tags:
|
|
34
|
-
|
|
37
|
+
try:
|
|
38
|
+
key, value = tag.split("=")
|
|
39
|
+
except ValueError:
|
|
40
|
+
logger.error("Tag specification '%s' must contain '='", tag)
|
|
41
|
+
raise
|
|
35
42
|
tags_dict[key] = value
|
|
36
43
|
return tags_dict
|
|
37
44
|
|
|
38
45
|
|
|
39
46
|
def main() -> None:
|
|
40
|
-
parser = parser_with_common_options(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
parser.add_argument(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
parser.add_argument(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
parser.add_argument(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
47
|
+
parser = parser_with_common_options(
|
|
48
|
+
provisioner_options=True, jobstore_option=False, prog="toil launch-cluster"
|
|
49
|
+
)
|
|
50
|
+
parser.add_argument(
|
|
51
|
+
"-T",
|
|
52
|
+
"--clusterType",
|
|
53
|
+
dest="clusterType",
|
|
54
|
+
choices=["mesos", "kubernetes"],
|
|
55
|
+
default=None, # TODO: change default to "kubernetes" when we are ready.
|
|
56
|
+
help="Cluster scheduler to use.",
|
|
57
|
+
)
|
|
58
|
+
parser.add_argument(
|
|
59
|
+
"--leaderNodeType",
|
|
60
|
+
dest="leaderNodeType",
|
|
61
|
+
required=True,
|
|
62
|
+
help="Non-preemptible node type to use for the cluster leader.",
|
|
63
|
+
)
|
|
64
|
+
parser.add_argument(
|
|
65
|
+
"--keyPairName",
|
|
66
|
+
dest="keyPairName",
|
|
67
|
+
help="On AWS, the name of the AWS key pair to include on the instance."
|
|
68
|
+
" On Google/GCE, this is the ssh key pair.",
|
|
69
|
+
)
|
|
70
|
+
parser.add_argument(
|
|
71
|
+
"--owner",
|
|
72
|
+
dest="owner",
|
|
73
|
+
help="The owner tag for all instances. If not given, the value in"
|
|
74
|
+
"TOIL_OWNER_TAG will be used, or else the value of --keyPairName.",
|
|
75
|
+
)
|
|
76
|
+
parser.add_argument(
|
|
77
|
+
"--boto",
|
|
78
|
+
dest="botoPath",
|
|
79
|
+
help="The path to the boto credentials directory. This is transferred "
|
|
80
|
+
"to all nodes in order to access the AWS jobStore from non-AWS instances.",
|
|
81
|
+
)
|
|
82
|
+
parser.add_argument(
|
|
83
|
+
"-t",
|
|
84
|
+
"--tag",
|
|
85
|
+
metavar="NAME=VALUE",
|
|
86
|
+
dest="tags",
|
|
87
|
+
default=[],
|
|
88
|
+
action="append",
|
|
89
|
+
help="Tags are added to the AWS cluster for this node and all of its "
|
|
90
|
+
"children. Tags are of the form:\n"
|
|
91
|
+
" -t key1=value1 --tag key2=value2\n"
|
|
92
|
+
"Multiple tags are allowed and each tag needs its own flag. By "
|
|
93
|
+
"default the cluster is tagged with "
|
|
94
|
+
" {\n"
|
|
95
|
+
' "Name": clusterName,\n'
|
|
96
|
+
' "Owner": IAM username\n'
|
|
97
|
+
" }. ",
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
parser.add_argument(
|
|
101
|
+
"--network", help="GCE cloud network to use. default: 'default'"
|
|
102
|
+
)
|
|
103
|
+
parser.add_argument(
|
|
104
|
+
"--vpcSubnet",
|
|
105
|
+
help="VPC subnet ID to launch cluster leader in. Uses default subnet "
|
|
106
|
+
"if not specified. This subnet needs to have auto assign IPs turned on.",
|
|
107
|
+
)
|
|
108
|
+
parser.add_argument(
|
|
109
|
+
"--use_private_ip",
|
|
110
|
+
dest="use_private_ip",
|
|
111
|
+
action="store_true",
|
|
112
|
+
default=False,
|
|
113
|
+
help="if specified, ignore the public ip of the nodes",
|
|
114
|
+
)
|
|
115
|
+
parser.add_argument(
|
|
116
|
+
"--nodeTypes",
|
|
117
|
+
dest="nodeTypes",
|
|
118
|
+
default=None,
|
|
119
|
+
type=str,
|
|
120
|
+
help="Specifies a list of comma-separated node types, each of which is "
|
|
121
|
+
"composed of slash-separated instance types, and an optional spot "
|
|
122
|
+
"bid set off by a colon, making the node type preemptible. Instance "
|
|
123
|
+
"types may appear in multiple node types, and the same node type "
|
|
124
|
+
"may appear as both preemptible and non-preemptible.\n"
|
|
125
|
+
"Valid argument specifying two node types:\n"
|
|
126
|
+
"\tc5.4xlarge/c5a.4xlarge:0.42,t2.large\n"
|
|
127
|
+
"Node types:\n"
|
|
128
|
+
"\tc5.4xlarge/c5a.4xlarge:0.42 and t2.large\n"
|
|
129
|
+
"Instance types:\n"
|
|
130
|
+
"\tc5.4xlarge, c5a.4xlarge, and t2.large\n"
|
|
131
|
+
"Semantics:\n"
|
|
132
|
+
"\tBid $0.42/hour for either c5.4xlarge or c5a.4xlarge instances,\n"
|
|
133
|
+
"\ttreated interchangeably, while they are available at that price,\n"
|
|
134
|
+
"\tand buy t2.large instances at full price\n"
|
|
135
|
+
"Must also provide the --workers argument to specify how many "
|
|
136
|
+
"workers of each node type to create.",
|
|
137
|
+
)
|
|
138
|
+
parser.add_argument(
|
|
139
|
+
"-w",
|
|
140
|
+
"--workers",
|
|
141
|
+
dest="workers",
|
|
142
|
+
default=None,
|
|
143
|
+
type=str,
|
|
144
|
+
help="Comma-separated list of the ranges of numbers of workers of each "
|
|
145
|
+
"node type to launch, such as '0-2,5,1-3'. If a range is given, "
|
|
146
|
+
"workers will automatically be launched and terminated by the cluster "
|
|
147
|
+
"to auto-scale to the workload.",
|
|
148
|
+
)
|
|
149
|
+
parser.add_argument(
|
|
150
|
+
"--leaderStorage",
|
|
151
|
+
dest="leaderStorage",
|
|
152
|
+
type=int,
|
|
153
|
+
default=50,
|
|
154
|
+
help="Specify the size (in gigabytes) of the root volume for the leader "
|
|
155
|
+
"instance. This is an EBS volume.",
|
|
156
|
+
)
|
|
157
|
+
parser.add_argument(
|
|
158
|
+
"--nodeStorage",
|
|
159
|
+
dest="nodeStorage",
|
|
160
|
+
type=int,
|
|
161
|
+
default=50,
|
|
162
|
+
help="Specify the size (in gigabytes) of the root volume for any worker "
|
|
163
|
+
"instances created when using the -w flag. This is an EBS volume.",
|
|
164
|
+
)
|
|
165
|
+
parser.add_argument(
|
|
166
|
+
"--forceDockerAppliance",
|
|
167
|
+
dest="forceDockerAppliance",
|
|
168
|
+
action="store_true",
|
|
169
|
+
default=False,
|
|
170
|
+
help="Disables sanity checking the existence of the docker image specified "
|
|
171
|
+
"by TOIL_APPLIANCE_SELF, which Toil uses to provision mesos for "
|
|
172
|
+
"autoscaling.",
|
|
173
|
+
)
|
|
174
|
+
parser.add_argument(
|
|
175
|
+
"--awsEc2ProfileArn",
|
|
176
|
+
dest="awsEc2ProfileArn",
|
|
177
|
+
default=None,
|
|
178
|
+
type=str,
|
|
179
|
+
help="If provided, the specified ARN is used as the instance profile for EC2 instances."
|
|
180
|
+
"Useful for setting custom IAM profiles. If not specified, a new IAM role is created "
|
|
181
|
+
"by default with sufficient access to perform basic cluster operations.",
|
|
182
|
+
)
|
|
183
|
+
parser.add_argument(
|
|
184
|
+
"--awsEc2ExtraSecurityGroupId",
|
|
185
|
+
dest="awsEc2ExtraSecurityGroupIds",
|
|
186
|
+
default=[],
|
|
187
|
+
action="append",
|
|
188
|
+
help="Any additional security groups to attach to EC2 instances. Note that a security group "
|
|
189
|
+
"with its name equal to the cluster name will always be created, thus ensure that "
|
|
190
|
+
"the extra security groups do not have the same name as the cluster name.",
|
|
191
|
+
)
|
|
192
|
+
parser.add_argument(
|
|
193
|
+
"--allowFuse",
|
|
194
|
+
type=opt_strtobool,
|
|
195
|
+
default=True,
|
|
196
|
+
help="Enable both the leader and worker nodes to be able to run Singularity with FUSE. For "
|
|
197
|
+
"Kubernetes, this will make the leader privileged and ask workers to run as privileged. "
|
|
198
|
+
"(default: %(default)s)",
|
|
199
|
+
)
|
|
200
|
+
# TODO Set Aws Profile in CLI options
|
|
118
201
|
options = parser.parse_args()
|
|
119
202
|
set_logging_from_options(options)
|
|
120
203
|
|
|
@@ -122,74 +205,94 @@ def main() -> None:
|
|
|
122
205
|
|
|
123
206
|
# Get worker node types
|
|
124
207
|
worker_node_types = parse_node_types(options.nodeTypes)
|
|
125
|
-
check_valid_node_types(
|
|
208
|
+
check_valid_node_types(
|
|
209
|
+
options.provisioner, worker_node_types + [({options.leaderNodeType}, None)]
|
|
210
|
+
)
|
|
126
211
|
|
|
127
212
|
# Holds string ranges, like "5", or "3-10"
|
|
128
|
-
worker_node_ranges = options.workers.split(
|
|
213
|
+
worker_node_ranges = options.workers.split(",") if options.workers else []
|
|
129
214
|
|
|
130
215
|
# checks the validity of TOIL_APPLIANCE_SELF before proceeding
|
|
131
216
|
applianceSelf(forceDockerAppliance=options.forceDockerAppliance)
|
|
132
217
|
|
|
133
218
|
# This holds either ints to launch static nodes, or tuples of ints
|
|
134
219
|
# specifying ranges to launch managed auto-scaling nodes, for each type.
|
|
135
|
-
nodeCounts:
|
|
220
|
+
nodeCounts: list[Union[int, tuple[int, int]]] = []
|
|
136
221
|
|
|
137
|
-
if (
|
|
138
|
-
|
|
222
|
+
if (worker_node_types != [] or worker_node_ranges != []) and not (
|
|
223
|
+
worker_node_types != [] and worker_node_ranges != []
|
|
224
|
+
):
|
|
139
225
|
raise RuntimeError("The --nodeTypes option requires --workers, and visa versa.")
|
|
140
226
|
if worker_node_types and worker_node_ranges:
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
227
|
+
if not len(worker_node_types) == len(worker_node_ranges):
|
|
228
|
+
raise RuntimeError(
|
|
229
|
+
"List of worker count ranges must be the same length as the list of node types."
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
for spec in worker_node_ranges:
|
|
233
|
+
if "-" in spec:
|
|
234
|
+
# Provision via autoscaling
|
|
235
|
+
parts = spec.split("-")
|
|
236
|
+
if len(parts) != 2:
|
|
237
|
+
raise RuntimeError("Unacceptable range: " + spec)
|
|
238
|
+
nodeCounts.append((int(parts[0]), int(parts[1])))
|
|
239
|
+
else:
|
|
240
|
+
# Provision fixed nodes
|
|
241
|
+
nodeCounts.append(int(spec))
|
|
242
|
+
|
|
243
|
+
owner = (
|
|
244
|
+
options.owner or os.getenv("TOIL_OWNER_TAG") or options.keyPairName or "toil"
|
|
245
|
+
)
|
|
156
246
|
|
|
157
247
|
# Check to see if the user specified a zone. If not, see if one is stored in an environment variable.
|
|
158
|
-
options.zone = options.zone or os.environ.get(
|
|
248
|
+
options.zone = options.zone or os.environ.get(
|
|
249
|
+
f"TOIL_{options.provisioner.upper()}_ZONE"
|
|
250
|
+
)
|
|
159
251
|
|
|
160
252
|
if not options.zone:
|
|
161
|
-
raise RuntimeError(
|
|
162
|
-
|
|
253
|
+
raise RuntimeError(
|
|
254
|
+
f"Please provide a value for --zone or set a default in the "
|
|
255
|
+
f"TOIL_{options.provisioner.upper()}_ZONE environment variable."
|
|
256
|
+
)
|
|
163
257
|
|
|
164
258
|
if options.clusterType == "mesos":
|
|
165
|
-
logger.warning(
|
|
166
|
-
|
|
167
|
-
|
|
259
|
+
logger.warning(
|
|
260
|
+
"You are using a Mesos cluster, which is no longer recommended as Toil is "
|
|
261
|
+
"transitioning to Kubernetes-based clusters. Consider switching to "
|
|
262
|
+
"--clusterType=kubernetes instead."
|
|
263
|
+
)
|
|
168
264
|
|
|
169
265
|
if options.clusterType is None:
|
|
170
|
-
logger.warning(
|
|
171
|
-
|
|
172
|
-
|
|
266
|
+
logger.warning(
|
|
267
|
+
'Argument --clusterType is not set... using "mesos". '
|
|
268
|
+
"In future versions of Toil, the default cluster scheduler will be "
|
|
269
|
+
'set to "kubernetes" if the cluster type is not specified.'
|
|
270
|
+
)
|
|
173
271
|
options.clusterType = "mesos"
|
|
174
272
|
|
|
175
|
-
logger.info(
|
|
176
|
-
|
|
177
|
-
cluster = cluster_factory(
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
273
|
+
logger.info("Creating cluster %s...", options.clusterName)
|
|
274
|
+
|
|
275
|
+
cluster = cluster_factory(
|
|
276
|
+
provisioner=options.provisioner,
|
|
277
|
+
clusterName=options.clusterName,
|
|
278
|
+
clusterType=options.clusterType,
|
|
279
|
+
zone=options.zone,
|
|
280
|
+
nodeStorage=options.nodeStorage,
|
|
281
|
+
enable_fuse=options.allowFuse,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
cluster.launchCluster(
|
|
285
|
+
leaderNodeType=options.leaderNodeType,
|
|
286
|
+
leaderStorage=options.leaderStorage,
|
|
287
|
+
owner=owner,
|
|
288
|
+
keyName=options.keyPairName,
|
|
289
|
+
botoPath=options.botoPath,
|
|
290
|
+
userTags=tags,
|
|
291
|
+
network=options.network,
|
|
292
|
+
vpcSubnet=options.vpcSubnet,
|
|
293
|
+
awsEc2ProfileArn=options.awsEc2ProfileArn,
|
|
294
|
+
awsEc2ExtraSecurityGroupIds=options.awsEc2ExtraSecurityGroupIds,
|
|
295
|
+
)
|
|
193
296
|
|
|
194
297
|
for typeNum, spec in enumerate(nodeCounts):
|
|
195
298
|
# For each batch of workers to make
|
|
@@ -207,8 +310,12 @@ def main() -> None:
|
|
|
207
310
|
cluster.addNodes(nodeTypes=wanted[0], numNodes=spec, preemptible=False)
|
|
208
311
|
else:
|
|
209
312
|
# We have a spot bid
|
|
210
|
-
cluster.addNodes(
|
|
211
|
-
|
|
313
|
+
cluster.addNodes(
|
|
314
|
+
nodeTypes=wanted[0],
|
|
315
|
+
numNodes=spec,
|
|
316
|
+
preemptible=True,
|
|
317
|
+
spotBid=wanted[1],
|
|
318
|
+
)
|
|
212
319
|
|
|
213
320
|
elif isinstance(spec, tuple):
|
|
214
321
|
# Make a range of auto-scaling nodes
|
|
@@ -225,11 +332,20 @@ def main() -> None:
|
|
|
225
332
|
|
|
226
333
|
if wanted[1] is None:
|
|
227
334
|
# Make non-spot instances
|
|
228
|
-
cluster.addManagedNodes(
|
|
229
|
-
|
|
335
|
+
cluster.addManagedNodes(
|
|
336
|
+
nodeTypes=wanted[0],
|
|
337
|
+
minNodes=min_count,
|
|
338
|
+
maxNodes=max_count,
|
|
339
|
+
preemptible=False,
|
|
340
|
+
)
|
|
230
341
|
else:
|
|
231
342
|
# Bid at the given price.
|
|
232
|
-
cluster.addManagedNodes(
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
343
|
+
cluster.addManagedNodes(
|
|
344
|
+
nodeTypes=wanted[0],
|
|
345
|
+
minNodes=min_count,
|
|
346
|
+
maxNodes=max_count,
|
|
347
|
+
preemptible=True,
|
|
348
|
+
spotBid=wanted[1],
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
logger.info("Cluster created successfully.")
|
toil/utils/toilMain.py
CHANGED
|
@@ -3,9 +3,8 @@ import re
|
|
|
3
3
|
import sys
|
|
4
4
|
import textwrap
|
|
5
5
|
import types
|
|
6
|
-
from typing import Any, Dict
|
|
7
|
-
|
|
8
6
|
from importlib.metadata import version as metadata_version
|
|
7
|
+
from typing import Any
|
|
9
8
|
|
|
10
9
|
from toil.version import version
|
|
11
10
|
|
|
@@ -13,23 +12,25 @@ from toil.version import version
|
|
|
13
12
|
def main() -> None:
|
|
14
13
|
modules = loadModules()
|
|
15
14
|
|
|
16
|
-
if len(sys.argv) < 2 or sys.argv[1] ==
|
|
15
|
+
if len(sys.argv) < 2 or sys.argv[1] == "--help":
|
|
17
16
|
printHelp(modules)
|
|
18
17
|
sys.exit(0)
|
|
19
18
|
|
|
20
19
|
cmd = sys.argv[1]
|
|
21
|
-
if cmd ==
|
|
20
|
+
if cmd == "--version":
|
|
22
21
|
printVersion()
|
|
23
22
|
sys.exit(0)
|
|
24
23
|
|
|
25
24
|
try:
|
|
26
25
|
module = modules[cmd]
|
|
27
26
|
except KeyError:
|
|
28
|
-
sys.stderr.write(
|
|
27
|
+
sys.stderr.write(
|
|
28
|
+
f'Unknown option "{cmd}". Pass --help to display usage information.\n'
|
|
29
|
+
)
|
|
29
30
|
sys.exit(1)
|
|
30
31
|
|
|
31
32
|
del sys.argv[1]
|
|
32
|
-
get_or_die(module,
|
|
33
|
+
get_or_die(module, "main")()
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
def get_or_die(module: types.ModuleType, name: str) -> Any:
|
|
@@ -39,12 +40,14 @@ def get_or_die(module: types.ModuleType, name: str) -> Any:
|
|
|
39
40
|
if hasattr(module, name):
|
|
40
41
|
return getattr(module, name)
|
|
41
42
|
else:
|
|
42
|
-
sys.stderr.write(
|
|
43
|
-
|
|
43
|
+
sys.stderr.write(
|
|
44
|
+
f"Internal Toil error!\nToil utility module "
|
|
45
|
+
f"{module.__name__} is missing required attribute {name}\n"
|
|
46
|
+
)
|
|
44
47
|
sys.exit(1)
|
|
45
48
|
|
|
46
49
|
|
|
47
|
-
def loadModules() ->
|
|
50
|
+
def loadModules() -> dict[str, types.ModuleType]:
|
|
48
51
|
# noinspection PyUnresolvedReferences
|
|
49
52
|
from toil.utils import toilClean # noqa
|
|
50
53
|
from toil.utils import toilConfig # noqa
|
|
@@ -59,13 +62,22 @@ def loadModules() -> Dict[str, types.ModuleType]:
|
|
|
59
62
|
from toil.utils import toilStats # noqa
|
|
60
63
|
from toil.utils import toilStatus # noqa
|
|
61
64
|
|
|
62
|
-
return {
|
|
65
|
+
return {
|
|
66
|
+
"-".join([i.lower() for i in re.findall("[A-Z][^A-Z]*", name)]): module
|
|
67
|
+
for name, module in locals().items()
|
|
68
|
+
}
|
|
63
69
|
|
|
64
70
|
|
|
65
|
-
def printHelp(modules:
|
|
71
|
+
def printHelp(modules: dict[str, types.ModuleType]) -> None:
|
|
66
72
|
name = os.path.basename(sys.argv[0])
|
|
67
|
-
descriptions =
|
|
68
|
-
|
|
73
|
+
descriptions = "\n ".join(
|
|
74
|
+
f'{cmd} - {get_or_die(mod, "__doc__").strip()}'
|
|
75
|
+
for cmd, mod in modules.items()
|
|
76
|
+
if mod
|
|
77
|
+
)
|
|
78
|
+
print(
|
|
79
|
+
textwrap.dedent(
|
|
80
|
+
f"""
|
|
69
81
|
Usage: {name} COMMAND ...
|
|
70
82
|
{name} --help
|
|
71
83
|
{name} COMMAND --help
|
|
@@ -73,11 +85,20 @@ def printHelp(modules: Dict[str, types.ModuleType]) -> None:
|
|
|
73
85
|
Where COMMAND is one of the following:
|
|
74
86
|
|
|
75
87
|
{descriptions}
|
|
76
|
-
"""[
|
|
88
|
+
"""[
|
|
89
|
+
1:
|
|
90
|
+
]
|
|
91
|
+
)
|
|
92
|
+
)
|
|
77
93
|
|
|
78
94
|
|
|
79
95
|
def printVersion() -> None:
|
|
80
96
|
try:
|
|
81
|
-
|
|
97
|
+
detected_version = metadata_version("toil")
|
|
98
|
+
if "a" in detected_version:
|
|
99
|
+
# This probably means Toil is installed as development
|
|
100
|
+
print(version)
|
|
101
|
+
else:
|
|
102
|
+
print(detected_version)
|
|
82
103
|
except:
|
|
83
|
-
print(f
|
|
104
|
+
print(f"Version gathered from toil.version: {version}")
|
toil/utils/toilRsyncCluster.py
CHANGED
|
@@ -14,27 +14,45 @@
|
|
|
14
14
|
"""Rsyncs into the toil appliance container running on the leader of the cluster."""
|
|
15
15
|
import argparse
|
|
16
16
|
import logging
|
|
17
|
+
import sys
|
|
17
18
|
|
|
18
19
|
from toil.common import parser_with_common_options
|
|
19
|
-
from toil.provisioners import cluster_factory
|
|
20
|
+
from toil.provisioners import NoSuchClusterException, cluster_factory
|
|
20
21
|
from toil.statsAndLogging import set_logging_from_options
|
|
21
22
|
|
|
22
23
|
logger = logging.getLogger(__name__)
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
def main() -> None:
|
|
26
|
-
parser = parser_with_common_options(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
parser.add_argument(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
parser = parser_with_common_options(
|
|
28
|
+
provisioner_options=True, jobstore_option=False, prog="toil rsync-cluster"
|
|
29
|
+
)
|
|
30
|
+
parser.add_argument(
|
|
31
|
+
"--insecure",
|
|
32
|
+
dest="insecure",
|
|
33
|
+
action="store_true",
|
|
34
|
+
required=False,
|
|
35
|
+
help="Temporarily disable strict host key checking.",
|
|
36
|
+
)
|
|
37
|
+
parser.add_argument(
|
|
38
|
+
"args",
|
|
39
|
+
nargs=argparse.REMAINDER,
|
|
40
|
+
help="Arguments to pass to"
|
|
41
|
+
"`rsync`. Takes any arguments that rsync accepts. Specify the"
|
|
42
|
+
" remote with a colon. For example, to upload `example.py`,"
|
|
43
|
+
" specify `toil rsync-cluster -p aws test-cluster example.py :`."
|
|
44
|
+
"\nOr, to download a file from the remote:, `toil rsync-cluster"
|
|
45
|
+
" -p aws test-cluster :example.py .`",
|
|
46
|
+
)
|
|
35
47
|
options = parser.parse_args()
|
|
36
48
|
set_logging_from_options(options)
|
|
37
|
-
cluster = cluster_factory(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
49
|
+
cluster = cluster_factory(
|
|
50
|
+
provisioner=options.provisioner,
|
|
51
|
+
clusterName=options.clusterName,
|
|
52
|
+
zone=options.zone,
|
|
53
|
+
)
|
|
54
|
+
try:
|
|
55
|
+
cluster.getLeader().coreRsync(args=options.args, strict=not options.insecure)
|
|
56
|
+
except NoSuchClusterException as e:
|
|
57
|
+
logger.error(e)
|
|
58
|
+
sys.exit(1)
|