Skip to content

Commit df6a89f

Browse files
authored
Merge pull request #7194 from chrisburr/safer-set-nproc
[v8.1] Make Job.setNumberOfProcessors safer to use
2 parents 1a9a5e3 + 6066dee commit df6a89f

File tree

2 files changed

+60
-58
lines changed

2 files changed

+60
-58
lines changed

src/DIRAC/Interfaces/API/Job.py

Lines changed: 38 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@
4848
COMPONENT_NAME = "/Interfaces/API/Job"
4949

5050

51+
class BadJobParameterError(ValueError):
52+
pass
53+
54+
5155
class Job(API):
5256
"""DIRAC jobs"""
5357

@@ -517,7 +521,10 @@ def setDestination(self, destination):
517521

518522
#############################################################################
519523
def setNumberOfProcessors(self, numberOfProcessors=None, minNumberOfProcessors=None, maxNumberOfProcessors=None):
520-
"""Helper function.
524+
"""Helper function to set the number of processors required by the job.
525+
526+
The DIRAC_JOB_PROCESSORS environment variable can be used by the job to
527+
determine how many processors are actually assigned.
521528
522529
Example usage:
523530
@@ -551,75 +558,50 @@ def setNumberOfProcessors(self, numberOfProcessors=None, minNumberOfProcessors=N
551558
552559
>>> job = Job()
553560
>>> job.setNumberOfProcessors(numberOfProcessors=3, maxNumberOfProcessors=4)
554-
will lead to ignore the second parameter
561+
will result in a BadJobParameterError
555562
556563
>>> job = Job()
557564
>>> job.setNumberOfProcessors(numberOfProcessors=3, minNumberOfProcessors=2)
558-
will lead to ignore the second parameter
565+
will result in a BadJobParameterError
559566
560567
:param int processors: number of processors required by the job (exact number, unless a min/max are set)
561568
:param int minNumberOfProcessors: optional min number of processors the job applications can use
562569
:param int maxNumberOfProcessors: optional max number of processors the job applications can use
563570
564-
:return: S_OK/S_ERROR
565-
"""
566-
if numberOfProcessors:
567-
if not minNumberOfProcessors:
568-
nProc = numberOfProcessors
569-
else:
570-
nProc = max(numberOfProcessors, minNumberOfProcessors)
571-
if nProc > 1:
572-
self._addParameter(
573-
self.workflow, "NumberOfProcessors", "JDL", nProc, "Exact number of processors requested"
574-
)
575-
self._addParameter(
576-
self.workflow,
577-
"MaxNumberOfProcessors",
578-
"JDL",
579-
nProc,
580-
"Max Number of processors the job applications may use",
581-
)
582-
return S_OK()
571+
:return: S_OK
583572
584-
if maxNumberOfProcessors and not minNumberOfProcessors:
585-
minNumberOfProcessors = 1
573+
:raises BadJobParameterError: If the function arguments are not valid.
574+
"""
575+
# If min and max are the same then that's the same as setting numberOfProcessors
576+
if minNumberOfProcessors and maxNumberOfProcessors and minNumberOfProcessors == maxNumberOfProcessors:
577+
numberOfProcessors = minNumberOfProcessors
578+
minNumberOfProcessors = maxNumberOfProcessors = None
586579

587-
if minNumberOfProcessors and maxNumberOfProcessors and minNumberOfProcessors >= maxNumberOfProcessors:
588-
minNumberOfProcessors = maxNumberOfProcessors
580+
if numberOfProcessors is not None:
581+
if minNumberOfProcessors is not None:
582+
raise BadJobParameterError("minNumberOfProcessors cannot be used with numberOfProcessors")
583+
if maxNumberOfProcessors is not None:
584+
raise BadJobParameterError("maxNumberOfProcessors cannot be used with numberOfProcessors")
589585

590-
if (
591-
minNumberOfProcessors
592-
and maxNumberOfProcessors
593-
and minNumberOfProcessors == maxNumberOfProcessors
594-
and minNumberOfProcessors > 1
595-
):
596586
self._addParameter(
597-
self.workflow,
598-
"NumberOfProcessors",
599-
"JDL",
600-
minNumberOfProcessors,
601-
"Exact number of processors requested",
587+
self.workflow, "NumberOfProcessors", "JDL", numberOfProcessors, "Exact number of processors requested"
602588
)
603589
self._addParameter(
604590
self.workflow,
605591
"MaxNumberOfProcessors",
606592
"JDL",
607-
minNumberOfProcessors,
593+
numberOfProcessors,
608594
"Max Number of processors the job applications may use",
609595
)
610596
return S_OK()
611597

612-
# By this point there should be a min
613-
self._addParameter(
614-
self.workflow,
615-
"MinNumberOfProcessors",
616-
"JDL",
617-
minNumberOfProcessors,
618-
"Min Number of processors the job applications may use",
619-
)
598+
if minNumberOfProcessors is None and maxNumberOfProcessors is None:
599+
return S_OK()
620600

621-
# If not set, will be "all"
622-
if maxNumberOfProcessors:
601+
minNumberOfProcessors = minNumberOfProcessors or 1
602+
if maxNumberOfProcessors is not None:
603+
if maxNumberOfProcessors < minNumberOfProcessors:
604+
raise BadJobParameterError("minNumberOfProcessors must be less than or equal to maxNumberOfProcessors")
623605
self._addParameter(
624606
self.workflow,
625607
"MaxNumberOfProcessors",
@@ -628,6 +610,14 @@ def setNumberOfProcessors(self, numberOfProcessors=None, minNumberOfProcessors=N
628610
"Max Number of processors the job applications may use",
629611
)
630612

613+
self._addParameter(
614+
self.workflow,
615+
"MinNumberOfProcessors",
616+
"JDL",
617+
minNumberOfProcessors,
618+
"Min Number of processors the job applications may use",
619+
)
620+
631621
return S_OK()
632622

633623
#############################################################################

src/DIRAC/Interfaces/API/test/Test_JobAPI.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import pytest
99

1010
from DIRAC.Core.Utilities.ClassAd.ClassAdLight import ClassAd
11-
from DIRAC.Interfaces.API.Job import Job
11+
from DIRAC.Interfaces.API.Job import Job, BadJobParameterError
1212

1313

1414
def test_basicJob():
@@ -74,31 +74,43 @@ def test_SimpleParametricJob():
7474
"proc, minProc, maxProc, expectedProc, expectedMinProc, expectedMaxProc",
7575
[
7676
(4, None, None, 4, None, 4),
77-
(4, 2, None, 4, None, 4),
78-
(4, 2, 8, 4, None, 4),
79-
(4, 8, 6, 8, None, 8), # non-sense
8077
(None, 2, 8, None, 2, 8),
8178
(None, 1, None, None, 1, None),
8279
(None, None, 8, None, 1, 8),
8380
(None, 8, 8, 8, None, 8),
84-
(None, 12, 8, 8, None, 8), # non-sense
8581
],
8682
)
87-
def test_setNumberOfProcessors(proc, minProc, maxProc, expectedProc, expectedMinProc, expectedMaxProc):
88-
# Arrange
83+
def test_setNumberOfProcessors_successful(proc, minProc, maxProc, expectedProc, expectedMinProc, expectedMaxProc):
8984
job = Job()
90-
91-
# Act
9285
res = job.setNumberOfProcessors(proc, minProc, maxProc)
9386

94-
# Assert
9587
assert res["OK"], res["Message"]
9688
jobDescription = ClassAd(f"[{job._toJDL()}]")
9789
assert expectedProc == jobDescription.getAttributeInt("NumberOfProcessors")
9890
assert expectedMinProc == jobDescription.getAttributeInt("MinNumberOfProcessors")
9991
assert expectedMaxProc == jobDescription.getAttributeInt("MaxNumberOfProcessors")
10092

10193

94+
@pytest.mark.parametrize(
95+
"proc, minProc, maxProc",
96+
[
97+
(4, 2, None),
98+
(4, 2, 8),
99+
(4, 8, 6),
100+
(None, 12, 8),
101+
],
102+
)
103+
def test_setNumberOfProcessors_unsuccessful(proc, minProc, maxProc):
104+
job = Job()
105+
with pytest.raises(BadJobParameterError):
106+
job.setNumberOfProcessors(proc, minProc, maxProc)
107+
108+
jobDescription = ClassAd(f"[{job._toJDL()}]")
109+
assert jobDescription.getAttributeInt("NumberOfProcessors") is None
110+
assert jobDescription.getAttributeInt("MinNumberOfProcessors") is None
111+
assert jobDescription.getAttributeInt("MaxNumberOfProcessors") is None
112+
113+
102114
@pytest.mark.parametrize(
103115
"sites, expectedSites",
104116
[

0 commit comments

Comments
 (0)