lsst-ctrl-execute 28.2025.500__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.
- lsst/__init__.py +3 -0
- lsst/ctrl/__init__.py +3 -0
- lsst/ctrl/execute/__init__.py +27 -0
- lsst/ctrl/execute/allocationConfig.py +69 -0
- lsst/ctrl/execute/allocator.py +474 -0
- lsst/ctrl/execute/allocatorParser.py +230 -0
- lsst/ctrl/execute/condorConfig.py +75 -0
- lsst/ctrl/execute/condorInfoConfig.py +65 -0
- lsst/ctrl/execute/envString.py +50 -0
- lsst/ctrl/execute/findPackageFile.py +107 -0
- lsst/ctrl/execute/libexec/allocateNodes.py +93 -0
- lsst/ctrl/execute/libexec/dagIdInfo.py +60 -0
- lsst/ctrl/execute/libexec/qdelete.py +48 -0
- lsst/ctrl/execute/libexec/qstatus.py +59 -0
- lsst/ctrl/execute/namedClassFactory.py +57 -0
- lsst/ctrl/execute/pbsPlugin.py +124 -0
- lsst/ctrl/execute/qCommand.py +66 -0
- lsst/ctrl/execute/seqFile.py +64 -0
- lsst/ctrl/execute/slurmPlugin.py +488 -0
- lsst/ctrl/execute/templateWriter.py +50 -0
- lsst_ctrl_execute-28.2025.500.dist-info/METADATA +20 -0
- lsst_ctrl_execute-28.2025.500.dist-info/RECORD +24 -0
- lsst_ctrl_execute-28.2025.500.dist-info/WHEEL +4 -0
- lsst_ctrl_execute-28.2025.500.dist-info/entry_points.txt +5 -0
lsst/__init__.py
ADDED
lsst/ctrl/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#
|
|
2
|
+
# LSST Data Management System
|
|
3
|
+
# Copyright 2008-2016 LSST Corporation.
|
|
4
|
+
#
|
|
5
|
+
# This product includes software developed by the
|
|
6
|
+
# LSST Project (http://www.lsst.org/).
|
|
7
|
+
#
|
|
8
|
+
# This program is free software: you can redistribute it and/or modify
|
|
9
|
+
# it under the terms of the GNU General Public License as published by
|
|
10
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
11
|
+
# (at your option) any later version.
|
|
12
|
+
#
|
|
13
|
+
# This program is distributed in the hope that it will be useful,
|
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16
|
+
# GNU General Public License for more details.
|
|
17
|
+
#
|
|
18
|
+
# You should have received a copy of the LSST License Statement and
|
|
19
|
+
# the GNU General Public License along with this program. If not,
|
|
20
|
+
# see <http://www.lsstcorp.org/LegalNotices/>.
|
|
21
|
+
#
|
|
22
|
+
from importlib.metadata import version
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
from .version import * # type: ignore # noqa: F403
|
|
26
|
+
except ModuleNotFoundError:
|
|
27
|
+
__version__ = version("lsst.ctrl.execute")
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#
|
|
2
|
+
# LSST Data Management System
|
|
3
|
+
# Copyright 2008-2016 LSST Corporation.
|
|
4
|
+
#
|
|
5
|
+
# This product includes software developed by the
|
|
6
|
+
# LSST Project (http://www.lsst.org/).
|
|
7
|
+
#
|
|
8
|
+
# This program is free software: you can redistribute it and/or modify
|
|
9
|
+
# it under the terms of the GNU General Public License as published by
|
|
10
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
11
|
+
# (at your option) any later version.
|
|
12
|
+
#
|
|
13
|
+
# This program is distributed in the hope that it will be useful,
|
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16
|
+
# GNU General Public License for more details.
|
|
17
|
+
#
|
|
18
|
+
# You should have received a copy of the LSST License Statement and
|
|
19
|
+
# the GNU General Public License along with this program. If not,
|
|
20
|
+
# see <http://www.lsstcorp.org/LegalNotices/>.
|
|
21
|
+
#
|
|
22
|
+
|
|
23
|
+
import lsst.pex.config as pexConfig
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AllocatedPlatformConfig(pexConfig.Config):
|
|
27
|
+
"""Platform specific information"""
|
|
28
|
+
|
|
29
|
+
queue = pexConfig.Field(
|
|
30
|
+
doc="the scheduler queue to submit to", dtype=str, default="debug"
|
|
31
|
+
)
|
|
32
|
+
email = pexConfig.Field(
|
|
33
|
+
doc="line to add to the scheduler file to get email notification (if supported)",
|
|
34
|
+
dtype=str,
|
|
35
|
+
default=None,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
scratchDirectory = pexConfig.Field(
|
|
39
|
+
doc="directory on the remote system where the scheduler file is sent",
|
|
40
|
+
dtype=str,
|
|
41
|
+
default=None,
|
|
42
|
+
)
|
|
43
|
+
loginHostName = pexConfig.Field(
|
|
44
|
+
doc="the host to login and copy files to", dtype=str, default=None
|
|
45
|
+
)
|
|
46
|
+
utilityPath = pexConfig.Field(
|
|
47
|
+
doc="the directory containing the scheduler commands", dtype=str, default=None
|
|
48
|
+
)
|
|
49
|
+
totalCoresPerNode = pexConfig.Field(
|
|
50
|
+
doc="the TOTAL number of cores on each node", dtype=int, default=1
|
|
51
|
+
)
|
|
52
|
+
glideinShutdown = pexConfig.Field(
|
|
53
|
+
doc="number of seconds of inactivity before glideins are cancelled",
|
|
54
|
+
dtype=int,
|
|
55
|
+
default=3600,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class AllocationConfig(pexConfig.Config):
|
|
60
|
+
"""A pex_config file describing the platform specific information required
|
|
61
|
+
to fill out a scheduler file which will be used to submit a scheduler
|
|
62
|
+
request.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
# this is done on two levels instead of one for future expansion of this
|
|
66
|
+
# config class, which may require local attributes to be specified.
|
|
67
|
+
platform = pexConfig.ConfigField(
|
|
68
|
+
"platform allocation information", AllocatedPlatformConfig
|
|
69
|
+
)
|
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# LSST Data Management System
|
|
5
|
+
# Copyright 2008-2016 LSST Corporation.
|
|
6
|
+
#
|
|
7
|
+
# This product includes software developed by the
|
|
8
|
+
# LSST Project (http://www.lsst.org/).
|
|
9
|
+
#
|
|
10
|
+
# This program is free software: you can redistribute it and/or modify
|
|
11
|
+
# it under the terms of the GNU General Public License as published by
|
|
12
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
13
|
+
# (at your option) any later version.
|
|
14
|
+
#
|
|
15
|
+
# This program is distributed in the hope that it will be useful,
|
|
16
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
17
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
18
|
+
# GNU General Public License for more details.
|
|
19
|
+
#
|
|
20
|
+
# You should have received a copy of the LSST License Statement and
|
|
21
|
+
# the GNU General Public License along with this program. If not,
|
|
22
|
+
# see <http://www.lsstcorp.org/LegalNotices/>.
|
|
23
|
+
#
|
|
24
|
+
|
|
25
|
+
import logging
|
|
26
|
+
import os
|
|
27
|
+
import pwd
|
|
28
|
+
import sys
|
|
29
|
+
from datetime import datetime
|
|
30
|
+
from string import Template
|
|
31
|
+
|
|
32
|
+
from lsst.ctrl.execute.allocationConfig import AllocationConfig
|
|
33
|
+
from lsst.ctrl.execute.condorInfoConfig import CondorInfoConfig
|
|
34
|
+
from lsst.ctrl.execute.templateWriter import TemplateWriter
|
|
35
|
+
from lsst.resources import ResourcePath, ResourcePathExpression
|
|
36
|
+
|
|
37
|
+
_LOG = logging.getLogger(__name__)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Allocator:
|
|
41
|
+
"""A class which consolidates allocation pex_config information with
|
|
42
|
+
override information (obtained from the command line) and produces a
|
|
43
|
+
PBS file using these values.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
platform : `str`
|
|
48
|
+
the name of the platform to execute on
|
|
49
|
+
opts : `Config`
|
|
50
|
+
Config object containing options
|
|
51
|
+
condorInfoFileName : `lsst.resources.ResourcePathExpression`
|
|
52
|
+
Name of the file containing Config information
|
|
53
|
+
|
|
54
|
+
Raises
|
|
55
|
+
------
|
|
56
|
+
TypeError
|
|
57
|
+
If the condorInfoFileName is the wrong type.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def __init__(
|
|
61
|
+
self,
|
|
62
|
+
platform: str,
|
|
63
|
+
opts,
|
|
64
|
+
configuration,
|
|
65
|
+
condorInfoFileName: ResourcePathExpression,
|
|
66
|
+
):
|
|
67
|
+
"""Constructor
|
|
68
|
+
@param platform: target platform for PBS submission
|
|
69
|
+
@param opts: options to override
|
|
70
|
+
"""
|
|
71
|
+
self.opts = opts
|
|
72
|
+
self.defaults = {}
|
|
73
|
+
self.configuration = configuration
|
|
74
|
+
|
|
75
|
+
condorInfoConfig = CondorInfoConfig()
|
|
76
|
+
condorInfoConfig.loadFromStream(ResourcePath(condorInfoFileName).read())
|
|
77
|
+
|
|
78
|
+
self.platform = platform
|
|
79
|
+
|
|
80
|
+
# Look up the user's name and home and scratch directory in the
|
|
81
|
+
# $HOME/.lsst/condor-info.py file
|
|
82
|
+
user_name = None
|
|
83
|
+
user_home = None
|
|
84
|
+
user_scratch = None
|
|
85
|
+
for name in condorInfoConfig.platform:
|
|
86
|
+
if name == self.platform:
|
|
87
|
+
user_name = condorInfoConfig.platform[name].user.name
|
|
88
|
+
user_home = condorInfoConfig.platform[name].user.home
|
|
89
|
+
user_scratch = condorInfoConfig.platform[name].user.scratch
|
|
90
|
+
if user_scratch is None and "SCRATCH" in os.environ:
|
|
91
|
+
user_scratch = os.environ["SCRATCH"]
|
|
92
|
+
if user_name is None:
|
|
93
|
+
raise RuntimeError(
|
|
94
|
+
"error: %s does not specify user name for platform == %s"
|
|
95
|
+
% (condorInfoFileName, self.platform)
|
|
96
|
+
)
|
|
97
|
+
if user_home is None:
|
|
98
|
+
raise RuntimeError(
|
|
99
|
+
"error: %s does not specify user home for platform == %s"
|
|
100
|
+
% (condorInfoFileName, self.platform)
|
|
101
|
+
)
|
|
102
|
+
if user_scratch is None:
|
|
103
|
+
raise RuntimeError(
|
|
104
|
+
"error: %s does not specify user scratch for platform == %s"
|
|
105
|
+
% (condorInfoFileName, self.platform)
|
|
106
|
+
)
|
|
107
|
+
self.defaults["USER_NAME"] = user_name
|
|
108
|
+
self.defaults["USER_HOME"] = user_home
|
|
109
|
+
self.defaults["USER_SCRATCH"] = user_scratch
|
|
110
|
+
self.commandLineDefaults = {}
|
|
111
|
+
self.commandLineDefaults["NODE_COUNT"] = self.opts.nodeCount
|
|
112
|
+
self.commandLineDefaults["COLLECTOR"] = self.opts.collector
|
|
113
|
+
self.commandLineDefaults["CPORT"] = self.opts.collectorport
|
|
114
|
+
self.commandLineDefaults["CPUS"] = self.opts.cpus
|
|
115
|
+
self.commandLineDefaults["WALL_CLOCK"] = self.opts.maximumWallClock
|
|
116
|
+
self.commandLineDefaults["ACCOUNT"] = self.opts.account
|
|
117
|
+
self.commandLineDefaults["MEMPERCORE"] = 4096
|
|
118
|
+
self.commandLineDefaults["ALLOWEDAUTO"] = 500
|
|
119
|
+
self.commandLineDefaults["AUTOCPUS"] = 16
|
|
120
|
+
self.commandLineDefaults["QUEUE"] = self.opts.queue
|
|
121
|
+
self.load()
|
|
122
|
+
|
|
123
|
+
def createUniqueIdentifier(self):
|
|
124
|
+
"""Creates a unique file identifier, based on the user's name
|
|
125
|
+
and the time at which this method is invoked.
|
|
126
|
+
|
|
127
|
+
Returns
|
|
128
|
+
-------
|
|
129
|
+
ident : `str`
|
|
130
|
+
the new identifier
|
|
131
|
+
"""
|
|
132
|
+
# This naming scheme follows the conventions used for creating
|
|
133
|
+
# RUNID names. We've found this allows these files to be more
|
|
134
|
+
# easily located and shared with other users when debugging
|
|
135
|
+
# The tempfile.mkstemp method restricts the file to only the user,
|
|
136
|
+
# and does not guarantee a file name can that easily be identified.
|
|
137
|
+
now = datetime.now()
|
|
138
|
+
self.defaults["DATE_STRING"] = "%02d_%02d%02d" % (
|
|
139
|
+
now.year,
|
|
140
|
+
now.month,
|
|
141
|
+
now.day,
|
|
142
|
+
)
|
|
143
|
+
username = pwd.getpwuid(os.geteuid()).pw_name
|
|
144
|
+
ident = "%s_%02d_%02d%02d_%02d%02d%02d" % (
|
|
145
|
+
username,
|
|
146
|
+
now.year,
|
|
147
|
+
now.month,
|
|
148
|
+
now.day,
|
|
149
|
+
now.hour,
|
|
150
|
+
now.minute,
|
|
151
|
+
now.second,
|
|
152
|
+
)
|
|
153
|
+
return ident
|
|
154
|
+
|
|
155
|
+
def load(self):
|
|
156
|
+
"""Loads all values from configuration and command line overrides into
|
|
157
|
+
data structures suitable for use by the TemplateWriter object.
|
|
158
|
+
"""
|
|
159
|
+
tempLocalScratch = Template(self.configuration.platform.localScratch)
|
|
160
|
+
self.defaults["LOCAL_SCRATCH"] = tempLocalScratch.substitute(
|
|
161
|
+
USER_SCRATCH=self.defaults["USER_SCRATCH"]
|
|
162
|
+
)
|
|
163
|
+
self.defaults["SCHEDULER"] = self.configuration.platform.scheduler
|
|
164
|
+
|
|
165
|
+
def loadAllocationConfig(self, name: ResourcePathExpression, suffix):
|
|
166
|
+
"""Loads all values from allocationConfig and command line overrides
|
|
167
|
+
into data structures suitable for use by the TemplateWriter object.
|
|
168
|
+
"""
|
|
169
|
+
if not (name_ := ResourcePath(name)).exists():
|
|
170
|
+
raise RuntimeError("%s was not found." % name_)
|
|
171
|
+
allocationConfig = AllocationConfig()
|
|
172
|
+
allocationConfig.loadFromStream(name_.read())
|
|
173
|
+
|
|
174
|
+
self.defaults["QUEUE"] = allocationConfig.platform.queue
|
|
175
|
+
self.defaults["EMAIL_NOTIFICATION"] = allocationConfig.platform.email
|
|
176
|
+
self.defaults["HOST_NAME"] = allocationConfig.platform.loginHostName
|
|
177
|
+
|
|
178
|
+
self.defaults["UTILITY_PATH"] = allocationConfig.platform.utilityPath
|
|
179
|
+
|
|
180
|
+
if self.opts.glideinShutdown is None:
|
|
181
|
+
self.defaults["GLIDEIN_SHUTDOWN"] = str(
|
|
182
|
+
allocationConfig.platform.glideinShutdown
|
|
183
|
+
)
|
|
184
|
+
else:
|
|
185
|
+
self.defaults["GLIDEIN_SHUTDOWN"] = str(self.opts.glideinShutdown)
|
|
186
|
+
|
|
187
|
+
if self.opts.outputLog is not None:
|
|
188
|
+
self.defaults["OUTPUT_LOG"] = self.opts.outputLog
|
|
189
|
+
else:
|
|
190
|
+
self.defaults["OUTPUT_LOG"] = "glide.out"
|
|
191
|
+
|
|
192
|
+
if self.opts.errorLog is not None:
|
|
193
|
+
self.defaults["ERROR_LOG"] = self.opts.errorLog
|
|
194
|
+
else:
|
|
195
|
+
self.defaults["ERROR_LOG"] = "glide.err"
|
|
196
|
+
|
|
197
|
+
# This is the TOTAL number of cores in the job, not just the total
|
|
198
|
+
# of the cores you intend to use. In other words, the total available
|
|
199
|
+
# on a machine, times the number of machines.
|
|
200
|
+
totalCoresPerNode = allocationConfig.platform.totalCoresPerNode
|
|
201
|
+
self.commandLineDefaults["TOTAL_CORE_COUNT"] = (
|
|
202
|
+
self.opts.nodeCount * totalCoresPerNode
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
self.uniqueIdentifier = self.createUniqueIdentifier()
|
|
206
|
+
|
|
207
|
+
# write these pbs and config files to {LOCAL_DIR}/configs
|
|
208
|
+
self.configDir = os.path.join(
|
|
209
|
+
self.defaults["LOCAL_SCRATCH"],
|
|
210
|
+
self.defaults["DATE_STRING"],
|
|
211
|
+
self.uniqueIdentifier,
|
|
212
|
+
"configs",
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
self.submitFileName = os.path.join(
|
|
216
|
+
self.configDir, "alloc_%s.%s" % (self.uniqueIdentifier, suffix)
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
self.condorConfigFileName = os.path.join(
|
|
220
|
+
self.configDir, "condor_%s.config" % self.uniqueIdentifier
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
self.defaults["GENERATED_CONFIG"] = os.path.basename(self.condorConfigFileName)
|
|
224
|
+
self.defaults["CONFIGURATION_ID"] = self.uniqueIdentifier
|
|
225
|
+
return allocationConfig
|
|
226
|
+
|
|
227
|
+
def createSubmitFile(self, inputFile):
|
|
228
|
+
"""Creates a batch submit file using the file "input" as a Template
|
|
229
|
+
|
|
230
|
+
Returns
|
|
231
|
+
-------
|
|
232
|
+
outfile : `str`
|
|
233
|
+
The newly created file name
|
|
234
|
+
"""
|
|
235
|
+
if not os.path.exists(self.configDir):
|
|
236
|
+
os.makedirs(self.configDir)
|
|
237
|
+
outfile = self.createFile(inputFile, self.submitFileName)
|
|
238
|
+
_LOG.debug("Wrote new Slurm submit file to %s", outfile)
|
|
239
|
+
return outfile
|
|
240
|
+
|
|
241
|
+
def createCondorConfigFile(self, input):
|
|
242
|
+
"""Creates a Condor config file using the file "input" as a Template
|
|
243
|
+
|
|
244
|
+
Returns
|
|
245
|
+
-------
|
|
246
|
+
outfile : `str`
|
|
247
|
+
The newly created file name
|
|
248
|
+
"""
|
|
249
|
+
outfile = self.createFile(input, self.condorConfigFileName)
|
|
250
|
+
_LOG.debug("Wrote new condor configuration file to %s", outfile)
|
|
251
|
+
return outfile
|
|
252
|
+
|
|
253
|
+
def createFile(self, input: ResourcePathExpression, output: ResourcePathExpression):
|
|
254
|
+
"""Creates a new file, using "input" as a Template, and writes the
|
|
255
|
+
new file to output.
|
|
256
|
+
|
|
257
|
+
Returns
|
|
258
|
+
-------
|
|
259
|
+
outfile : `str`
|
|
260
|
+
The newly created file name
|
|
261
|
+
"""
|
|
262
|
+
_LOG.debug("Creating file from template using %s", input)
|
|
263
|
+
template = TemplateWriter()
|
|
264
|
+
# Uses the associative arrays of "defaults" and "commandLineDefaults"
|
|
265
|
+
# to write out the new file from the template.
|
|
266
|
+
# The commandLineDefaults override values in "defaults"
|
|
267
|
+
substitutes = self.defaults.copy()
|
|
268
|
+
for key in self.commandLineDefaults:
|
|
269
|
+
val = self.commandLineDefaults[key]
|
|
270
|
+
if val is not None:
|
|
271
|
+
substitutes[key] = self.commandLineDefaults[key]
|
|
272
|
+
template.rewrite(input, output, substitutes)
|
|
273
|
+
return output
|
|
274
|
+
|
|
275
|
+
def isVerbose(self):
|
|
276
|
+
"""Status of the verbose flag
|
|
277
|
+
@return True if the flag was set, False otherwise
|
|
278
|
+
"""
|
|
279
|
+
return self.opts.verbose
|
|
280
|
+
|
|
281
|
+
def isAuto(self):
|
|
282
|
+
"""Status of the auto flag
|
|
283
|
+
@return True if the flag was set, False otherwise
|
|
284
|
+
"""
|
|
285
|
+
return self.opts.auto
|
|
286
|
+
|
|
287
|
+
def getUserName(self):
|
|
288
|
+
"""Accessor for USER_NAME
|
|
289
|
+
@return the value of USER_NAME
|
|
290
|
+
"""
|
|
291
|
+
return self.getParameter("USER_NAME")
|
|
292
|
+
|
|
293
|
+
def getUserHome(self):
|
|
294
|
+
"""Accessor for USER_HOME
|
|
295
|
+
@return the value of USER_HOME
|
|
296
|
+
"""
|
|
297
|
+
return self.getParameter("USER_HOME")
|
|
298
|
+
|
|
299
|
+
def getUserScratch(self):
|
|
300
|
+
"""Accessor for USER_SCRATCH
|
|
301
|
+
@return the value of USER_SCRATCH
|
|
302
|
+
"""
|
|
303
|
+
return self.getParameter("USER_SCRATCH")
|
|
304
|
+
|
|
305
|
+
def getHostName(self):
|
|
306
|
+
"""Accessor for HOST_NAME
|
|
307
|
+
@return the value of HOST_NAME
|
|
308
|
+
"""
|
|
309
|
+
return self.getParameter("HOST_NAME")
|
|
310
|
+
|
|
311
|
+
def getUtilityPath(self):
|
|
312
|
+
"""Accessor for UTILITY_PATH
|
|
313
|
+
@return the value of UTILITY_PATH
|
|
314
|
+
"""
|
|
315
|
+
return self.getParameter("UTILITY_PATH")
|
|
316
|
+
|
|
317
|
+
def getScratchDirectory(self):
|
|
318
|
+
"""Accessor for SCRATCH_DIR
|
|
319
|
+
@return the value of SCRATCH_DIR
|
|
320
|
+
"""
|
|
321
|
+
return self.getParameter("SCRATCH_DIR")
|
|
322
|
+
|
|
323
|
+
def getLocalScratchDirectory(self):
|
|
324
|
+
"""Accessor for LOCAL_SCRATCH
|
|
325
|
+
@return the value of LOCAL_SCRATCH
|
|
326
|
+
"""
|
|
327
|
+
return self.getParameter("LOCAL_SCRATCH")
|
|
328
|
+
|
|
329
|
+
def getNodeSetName(self):
|
|
330
|
+
"""Accessor for NODE_SET
|
|
331
|
+
@return the value of NODE_SET
|
|
332
|
+
"""
|
|
333
|
+
return self.getParameter("NODE_SET")
|
|
334
|
+
|
|
335
|
+
def getNodes(self):
|
|
336
|
+
"""Accessor for NODE_COUNT
|
|
337
|
+
@return the value of NODE_COUNT
|
|
338
|
+
"""
|
|
339
|
+
return self.getParameter("NODE_COUNT")
|
|
340
|
+
|
|
341
|
+
def getMemoryPerCore(self):
|
|
342
|
+
"""Accessor for MemoryPerCore
|
|
343
|
+
@return the value of MemoryPerCore
|
|
344
|
+
"""
|
|
345
|
+
return self.getParameter("MEMPERCORE")
|
|
346
|
+
|
|
347
|
+
def getAllowedAutoGlideins(self):
|
|
348
|
+
"""Accessor for AllowedAutoGlideins
|
|
349
|
+
@return the value of AllowedAuto
|
|
350
|
+
"""
|
|
351
|
+
return self.getParameter("ALLOWEDAUTO")
|
|
352
|
+
|
|
353
|
+
def getQOS(self):
|
|
354
|
+
"""Accessor for QOS
|
|
355
|
+
@return the value of QOS
|
|
356
|
+
"""
|
|
357
|
+
return self.getParameter("QOS")
|
|
358
|
+
|
|
359
|
+
def getCPUs(self):
|
|
360
|
+
"""Accessor for CPUS
|
|
361
|
+
@return the value of CPUS
|
|
362
|
+
"""
|
|
363
|
+
return self.getParameter("CPUS")
|
|
364
|
+
|
|
365
|
+
def getAutoCPUs(self):
|
|
366
|
+
"""Size of standard glideins for allocateNodes auto
|
|
367
|
+
@return the value of autoCPUs
|
|
368
|
+
"""
|
|
369
|
+
return self.getParameter("AUTOCPUS")
|
|
370
|
+
|
|
371
|
+
def getWallClock(self):
|
|
372
|
+
"""Accessor for WALL_CLOCK
|
|
373
|
+
@return the value of WALL_CLOCK
|
|
374
|
+
"""
|
|
375
|
+
return self.getParameter("WALL_CLOCK")
|
|
376
|
+
|
|
377
|
+
def getScheduler(self):
|
|
378
|
+
"""Accessor for SCHEDULER
|
|
379
|
+
@return the value of SCHEDULER
|
|
380
|
+
"""
|
|
381
|
+
return self.getParameter("SCHEDULER")
|
|
382
|
+
|
|
383
|
+
def getReservation(self):
|
|
384
|
+
"""Accessor for RESERVATION
|
|
385
|
+
@return the value of RESERVATION
|
|
386
|
+
"""
|
|
387
|
+
return self.getParameter("RESERVATION")
|
|
388
|
+
|
|
389
|
+
def getParameter(self, value):
|
|
390
|
+
"""Accessor for generic value
|
|
391
|
+
@return None if value is not set. Otherwise, use the command line
|
|
392
|
+
override (if set), or the default Config value
|
|
393
|
+
"""
|
|
394
|
+
if value in self.commandLineDefaults:
|
|
395
|
+
return self.commandLineDefaults[value]
|
|
396
|
+
if value in self.defaults:
|
|
397
|
+
return self.defaults[value]
|
|
398
|
+
return None
|
|
399
|
+
|
|
400
|
+
def printNodeSetInfo(self):
|
|
401
|
+
nodes = self.getNodes()
|
|
402
|
+
cpus = self.getCPUs()
|
|
403
|
+
wallClock = self.getWallClock()
|
|
404
|
+
nodeString = ""
|
|
405
|
+
|
|
406
|
+
if int(nodes) > 1:
|
|
407
|
+
nodeString = "s"
|
|
408
|
+
if self.opts.dynamic is None:
|
|
409
|
+
print(
|
|
410
|
+
"%s glidein%s will be allocated on %s using default dynamic slots configuration."
|
|
411
|
+
% (nodes, nodeString, self.platform)
|
|
412
|
+
)
|
|
413
|
+
print(
|
|
414
|
+
"There will be %s cores per glidein and a maximum time limit of %s"
|
|
415
|
+
% (cpus, wallClock)
|
|
416
|
+
)
|
|
417
|
+
elif self.opts.dynamic == "__default__":
|
|
418
|
+
print(
|
|
419
|
+
"%s glidein%s will be allocated on %s using default dynamic slots configuration."
|
|
420
|
+
% (nodes, nodeString, self.platform)
|
|
421
|
+
)
|
|
422
|
+
print(
|
|
423
|
+
"There will be %s cores per glidein and a maximum time limit of %s"
|
|
424
|
+
% (cpus, wallClock)
|
|
425
|
+
)
|
|
426
|
+
else:
|
|
427
|
+
print(
|
|
428
|
+
"%s node%s will be allocated on %s using dynamic slot block specified in '%s'"
|
|
429
|
+
% (nodes, nodeString, self.platform, self.opts.dynamic)
|
|
430
|
+
)
|
|
431
|
+
print(
|
|
432
|
+
"There will be %s cores per node and maximum time limit of %s"
|
|
433
|
+
% (cpus, wallClock)
|
|
434
|
+
)
|
|
435
|
+
print("Node set name:")
|
|
436
|
+
print(self.getNodeSetName())
|
|
437
|
+
|
|
438
|
+
def runCommand(self, cmd, verbose):
|
|
439
|
+
cmd_split = cmd.split()
|
|
440
|
+
pid = os.fork()
|
|
441
|
+
if not pid:
|
|
442
|
+
# Methods of file transfer and login may
|
|
443
|
+
# produce different output, depending on how
|
|
444
|
+
# the "gsi" utilities are used. The user can
|
|
445
|
+
# either use grid proxies or ssh, and gsiscp/gsissh
|
|
446
|
+
# does the right thing. Since the output will be
|
|
447
|
+
# different in either case anything potentially parsing this
|
|
448
|
+
# output (like drpRun), would have to go through extra
|
|
449
|
+
# steps to deal with this output, and which ultimately
|
|
450
|
+
# end up not being useful. So we optinally close the i/o output
|
|
451
|
+
# of the executing command down.
|
|
452
|
+
#
|
|
453
|
+
# stdin/stdio/stderr is treated specially
|
|
454
|
+
# by python, so we have to close down
|
|
455
|
+
# both the python objects and the
|
|
456
|
+
# underlying c implementations
|
|
457
|
+
if not verbose:
|
|
458
|
+
# close python i/o
|
|
459
|
+
sys.stdin.close()
|
|
460
|
+
sys.stdout.close()
|
|
461
|
+
sys.stderr.close()
|
|
462
|
+
# close C's i/o
|
|
463
|
+
os.close(0)
|
|
464
|
+
os.close(1)
|
|
465
|
+
os.close(2)
|
|
466
|
+
os.execvp(cmd_split[0], cmd_split)
|
|
467
|
+
pid, status = os.wait()
|
|
468
|
+
# high order bits are status, low order bits are signal.
|
|
469
|
+
exitCode = (status & 0xFF00) >> 8
|
|
470
|
+
return exitCode
|
|
471
|
+
|
|
472
|
+
def submit(self):
|
|
473
|
+
"""Submit the glidein jobs to the Batch system."""
|
|
474
|
+
raise NotImplementedError
|