Skip to content

Commit ed1c6f0

Browse files
feat: Add step information for executions of multiple steps at a time
1 parent 2d16150 commit ed1c6f0

8 files changed

Lines changed: 301 additions & 231 deletions

File tree

src/dirac_cwl/commands/analyze_xml_summary.py

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,42 +4,37 @@
44
import os
55

66
from LHCbDIRAC.Workflow.Modules.AnalyseXMLSummary import _areInputsOK, _isXMLSummaryOK
7-
from LHCbDIRAC.Workflow.Modules.BookkeepingReport import _generate_xml_object
87

98
from dirac_cwl.core.exceptions import WorkflowProcessingException
109

1110
from .core import PostProcessCommand
12-
from .workflow_commons import StepStatus, WorkflowCommons
11+
from .workflow_commons import Step, StepStatus, WorkflowCommons
1312

1413
logger = logging.getLogger(__name__)
1514

1615

1716
class AnalyseXmlSummary(PostProcessCommand):
1817
"""Performs a series of checks on the XMLSummary output to make sure the execution was done correctly."""
1918

20-
def _execute(self, job_path: os.PathLike, workflow_commons: WorkflowCommons, **kwargs) -> None:
19+
def _execute(self, job_path: os.PathLike, workflow_commons: WorkflowCommons, **kwargs):
2120
"""Execute the command.
2221
2322
:param job_path: Path to the job working directory.
24-
:param workflow_commons: WorkflowCommons object.
23+
:param workflow_commons: WorkflowCommons object
2524
:param kwargs: Additional keyword arguments.
2625
"""
27-
if not workflow_commons.xf_o:
28-
workflow_commons.xf_o = _generate_xml_object(
29-
workflow_commons.cleaned_application_name,
30-
workflow_commons.production_id,
31-
workflow_commons.prod_job_id,
32-
workflow_commons.step_number,
33-
workflow_commons.step_id,
34-
)
26+
for step in workflow_commons.steps:
27+
self._execute_for_step(job_path, workflow_commons, step, **kwargs)
3528

36-
jobOk = _isXMLSummaryOK(workflow_commons.xf_o)
29+
def _execute_for_step(self, job_path: os.PathLike, workflow_commons: WorkflowCommons, step_commons: Step, **kwargs):
30+
"""Execute the command for a specific step."""
31+
jobOk = _isXMLSummaryOK(step_commons.xf_o)
3732

3833
if jobOk:
3934
jobOk = _areInputsOK(
40-
workflow_commons.xf_o,
41-
workflow_commons.inputs,
42-
workflow_commons.number_of_events,
35+
step_commons.xf_o,
36+
step_commons.inputs,
37+
step_commons.number_of_events,
4338
workflow_commons.production_id,
4439
workflow_commons.file_report,
4540
)
@@ -51,4 +46,4 @@ def _execute(self, job_path: os.PathLike, workflow_commons: WorkflowCommons, **k
5146
logger.info("Workflow already failed")
5247
return
5348

54-
workflow_commons.job_report.setApplicationStatus(f"{workflow_commons.application_name} Step OK")
49+
workflow_commons.job_report.setApplicationStatus(f"{step_commons.application_name} Step OK")

src/dirac_cwl/commands/bookkeeping_report.py

Lines changed: 49 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""LHCb command for bookkeeping report file generation based on the XMLSummary and the XML catalog."""
22

3+
import copy
34
import logging
45
import os
56
from typing import Any, Dict
@@ -19,7 +20,7 @@
1920
from dirac_cwl.core.exceptions import WorkflowProcessingException
2021

2122
from .core import PostProcessCommand
22-
from .workflow_commons import StepStatus, WorkflowCommons
23+
from .workflow_commons import Step, StepStatus, WorkflowCommons
2324

2425
logger = logging.getLogger(__name__)
2526

@@ -34,29 +35,40 @@ def _execute(self, job_path: os.PathLike, workflow_commons: WorkflowCommons, **k
3435
:param workflow_commons: WorkflowCommons object
3536
:param kwargs: Additional keyword arguments.
3637
"""
37-
# Obtain Workflow Commons
38-
if workflow_commons.step_status == StepStatus.Failed:
39-
return
38+
for step in workflow_commons.steps:
39+
if workflow_commons.step_status == StepStatus.Failed:
40+
return
4041

42+
self._execute_for_step(job_path, workflow_commons, step, **kwargs)
43+
44+
def _execute_for_step(self, job_path: os.PathLike, workflow_commons: WorkflowCommons, step_commons: Step, **kwargs):
4145
# Setup variables
4246
cpu_times: Dict[str, Any] = {}
43-
if workflow_commons.start_time:
44-
cpu_times["StartTime"] = workflow_commons.start_time
45-
if workflow_commons.start_stats:
46-
cpu_times["StartStats"] = workflow_commons.start_stats
47+
if step_commons.start_time:
48+
cpu_times["StartTime"] = step_commons.start_time
49+
if step_commons.start_stats:
50+
cpu_times["StartStats"] = step_commons.start_stats
4751

4852
exectime, cputime = getStepCPUTimes(cpu_times)
4953

50-
number_of_processors = getNumberOfProcessorsToUse(
51-
workflow_commons.job_id,
52-
workflow_commons.max_number_of_processors,
53-
)
54+
number_of_processors = workflow_commons.number_of_processors
55+
56+
if (step_commons.multicore and workflow_commons.multicore) or (
57+
workflow_commons.job_type.lower() == "user" and workflow_commons.max_number_of_processors
58+
):
59+
number_of_processors = getNumberOfProcessorsToUse(
60+
workflow_commons.job_id,
61+
workflow_commons.max_number_of_processors,
62+
)
63+
64+
all_outputs = copy.deepcopy(step_commons.outputs)
65+
all_outputs.extend(step_commons.outputs)
5466

5567
parameters = {
5668
"PRODUCTION_ID": workflow_commons.production_id,
5769
"JOB_ID": workflow_commons.prod_job_id,
5870
"configVersion": workflow_commons.config_version,
59-
"outputList": workflow_commons.outputs,
71+
"outputList": all_outputs,
6072
"configName": workflow_commons.config_name,
6173
"outputDataFileMask": workflow_commons.output_data_file_mask,
6274
}
@@ -79,16 +91,16 @@ def _execute(self, job_path: os.PathLike, workflow_commons: WorkflowCommons, **k
7991

8092
bk_lfns = production_lfns_dict["BookkeepingLFNs"]
8193

82-
ldate, ltime, ldatestart, ltimestart = _process_time(workflow_commons.start_time)
94+
ldate, ltime, ldatestart, ltimestart = _process_time(step_commons.start_time)
8395

8496
# Obtain XMLSummary
85-
if not workflow_commons.xf_o:
86-
workflow_commons.xf_o = _generate_xml_object(
87-
workflow_commons.cleaned_application_name,
97+
if not step_commons.xf_o:
98+
step_commons.xf_o = _generate_xml_object(
99+
step_commons.cleaned_application_name,
88100
workflow_commons.production_id,
89101
workflow_commons.prod_job_id,
90-
workflow_commons.step_number,
91-
workflow_commons.step_id,
102+
step_commons.number,
103+
step_commons.id,
92104
)
93105

94106
info_dict = {
@@ -99,9 +111,9 @@ def _execute(self, job_path: os.PathLike, workflow_commons: WorkflowCommons, **k
99111
"jobID": workflow_commons.job_id,
100112
"siteName": workflow_commons.site_name,
101113
"jobType": workflow_commons.job_type,
102-
"applicationName": workflow_commons.application_name,
103-
"applicationVersion": workflow_commons.application_version,
104-
"numberOfEvents": workflow_commons.number_of_events,
114+
"applicationName": step_commons.application_name,
115+
"applicationVersion": step_commons.application_version,
116+
"numberOfEvents": step_commons.number_of_events,
105117
}
106118

107119
# Generate job_info object
@@ -111,37 +123,40 @@ def _execute(self, job_path: os.PathLike, workflow_commons: WorkflowCommons, **k
111123
ltimestart,
112124
ldate,
113125
ltime,
114-
workflow_commons.xf_o,
115-
workflow_commons.inputs,
116-
workflow_commons.step_id,
117-
workflow_commons.bk_step_id,
126+
step_commons.xf_o,
127+
step_commons.inputs,
128+
step_commons.id,
129+
step_commons.bk_id,
118130
workflow_commons.bk_client,
119131
workflow_commons.config_name,
120132
workflow_commons.config_version,
121133
)
122134

123135
# Add input files to job_info
124-
_generateInputFiles(job_info, bk_lfns, workflow_commons.inputs)
136+
_generateInputFiles(job_info, bk_lfns, step_commons.inputs)
125137

126138
# Add output files to job_info
127139
_generateOutputFiles(
128140
job_info,
129141
bk_lfns,
130-
workflow_commons.event_type,
131-
workflow_commons.application_name,
132-
workflow_commons.xf_o,
133-
workflow_commons.outputs,
134-
workflow_commons.inputs,
142+
step_commons.event_type,
143+
step_commons.application_name,
144+
step_commons.xf_o,
145+
step_commons.outputs,
146+
step_commons.inputs,
147+
step_commons.size,
148+
step_commons.md5,
149+
step_commons.guid,
135150
)
136151

137152
# Generate SimulationConditions
138-
if workflow_commons.application_name == "Gauss":
153+
if step_commons.application_name == "Gauss":
139154
job_info.simulation_condition = workflow_commons.sim_description
140155

141156
# Convert job_info object to XML
142157
doc = job_info.to_xml()
143158

144159
# Write to file
145-
bfilename = f"bookkeeping_{workflow_commons.step_id}.xml"
160+
bfilename = f"bookkeeping_{step_commons.id}.xml"
146161
with open(bfilename, "wb") as bfile:
147162
bfile.write(doc)

src/dirac_cwl/commands/core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def execute(self, job_path: os.PathLike, **kwargs) -> None:
2929
workflow_commons = None
3030
try:
3131
workflow_commons = WorkflowCommons.load(job_path)
32-
32+
logger.info("WorkflowCommons:\n%s", workflow_commons)
3333
self._execute(job_path, workflow_commons, **kwargs)
3434

3535
except WorkflowProcessingException:

src/dirac_cwl/commands/download_config.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44

55
from dirac_cwl.commands import PreProcessCommand
66

7+
from .workflow_commons import WorkflowCommons
8+
79

810
class DownloadConfig(PreProcessCommand):
911
"""Example command that creates a file with named 'content.cfg'."""
1012

11-
def execute(self, job_path, **kwargs):
13+
def _execute(self, job_path: os.PathLike, workflow_commons: WorkflowCommons, **kwargs):
1214
"""Execute the configuration download.
1315
1416
:param job_path: Path to the job working directory.

src/dirac_cwl/commands/group_outputs.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55

66
from dirac_cwl.commands import PostProcessCommand
77

8+
from .workflow_commons import WorkflowCommons
9+
810

911
class GroupOutputs(PostProcessCommand):
1012
"""Example command that merges all of the outputs in a singular file."""
1113

12-
def execute(self, job_path, **kwargs):
14+
def _execute(self, job_path: os.PathLike, workflow_commons: WorkflowCommons, **kwargs):
1315
"""Execute the output file grouping.
1416
1517
:param job_path: Path to the job working directory.

src/dirac_cwl/commands/workflow_accounting.py

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,34 @@
1616
from dirac_cwl.core.exceptions import WorkflowProcessingException
1717

1818
from .core import PostProcessCommand
19-
from .workflow_commons import WorkflowCommons
19+
from .workflow_commons import Step, WorkflowCommons
2020

2121
logger = logging.getLogger(__name__)
2222

2323

2424
class WorkflowAccounting(PostProcessCommand):
2525
"""Prepares and sends accounting information to the DIRAC Accounting system."""
2626

27-
def _execute(self, job_path: os.PathLike, workflow_commons: WorkflowCommons, **kwargs) -> None:
27+
def _execute(self, job_path: os.PathLike, workflow_commons: WorkflowCommons, **kwargs):
2828
"""Execute the command.
2929
3030
:param job_path: Path to the job working directory.
31-
:param workflow_commons: WorkflowCommons object.
31+
:param workflow_commons: WorkflowCommons object
3232
:param kwargs: Additional keyword arguments.
3333
"""
34-
# Obtain Workflow Commons
34+
for step in workflow_commons.steps:
35+
self._execute_for_step(job_path, workflow_commons, step, **kwargs)
36+
37+
def _execute_for_step(self, job_path: os.PathLike, workflow_commons: WorkflowCommons, step_commons: Step, **kwargs):
3538
cpu_times: Dict[str, Any] = {}
36-
if workflow_commons.start_time:
37-
cpu_times["StartTime"] = workflow_commons.start_time
38-
if workflow_commons.start_stats:
39-
cpu_times["StartStats"] = workflow_commons.start_stats
39+
if step_commons.start_time:
40+
cpu_times["StartTime"] = step_commons.start_time
41+
if step_commons.start_stats:
42+
cpu_times["StartStats"] = step_commons.start_stats
43+
44+
if not step_commons.application_name:
45+
logger.info("Not an application step: it will not be accounted")
46+
return
4047

4148
exec_time, cpu_time = getStepCPUTimes(cpu_times)
4249

@@ -45,7 +52,9 @@ def _execute(self, job_path: os.PathLike, workflow_commons: WorkflowCommons, **k
4552

4653
job_step = JobStep()
4754

48-
if not workflow_commons.xf_o:
55+
xf_o = step_commons.xf_o
56+
57+
if not xf_o:
4958
logger.error("XML Summary object could not be found (not produced?), skipping the report")
5059
return
5160

@@ -56,18 +65,18 @@ def _execute(self, job_path: os.PathLike, workflow_commons: WorkflowCommons, **k
5665
dataDict = {
5766
"JobGroup": str(workflow_commons.production_id),
5867
"RunNumber": workflow_commons.run_number,
59-
"EventType": workflow_commons.event_type,
60-
"ProcessingType": workflow_commons.step_proc_pass, # this is the processing pass of the step
61-
"ProcessingStep": workflow_commons.bk_step_id, # the step ID
68+
"EventType": step_commons.event_type,
69+
"ProcessingType": step_commons.proc_pass, # this is the processing pass of the step
70+
"ProcessingStep": step_commons.bk_id, # the step ID
6271
"Site": workflow_commons.site_name,
6372
"FinalStepState": workflow_commons.step_status,
6473
"CPUTime": cpu_time,
6574
"NormCPUTime": normCPU,
6675
"ExecTime": exec_time * workflow_commons.number_of_processors,
67-
"InputData": sum(workflow_commons.xf_o.inputFileStats.values()),
68-
"OutputData": sum(workflow_commons.xf_o.outputFileStats.values()),
69-
"InputEvents": workflow_commons.xf_o.inputEventsTotal,
70-
"OutputEvents": workflow_commons.xf_o.outputEventsTotal,
76+
"InputData": sum(xf_o.inputFileStats.values()),
77+
"OutputData": sum(xf_o.outputFileStats.values()),
78+
"InputEvents": xf_o.inputEventsTotal,
79+
"OutputEvents": xf_o.outputEventsTotal,
7180
}
7281

7382
job_step.setValuesFromDict(dataDict)

0 commit comments

Comments
 (0)