Skip to content

Commit a58e402

Browse files
committed
Merge branch 'main' into stress-ng_cpu_benchmark
2 parents 79d01ed + 97fc507 commit a58e402

12 files changed

Lines changed: 452 additions & 28 deletions

File tree

.github/workflows/build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ jobs:
3232
pip install docker
3333
pip install pygraphviz
3434
pip install pydot
35+
pip install igraph
3536
3637
- name: Check package install
3738
run: |

tests/test_helpers.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,24 @@ def _compare_workflows(workflow_1: Workflow, workflow_2: Workflow):
143143
# Test the number of tasks
144144
assert (len(workflow_1.tasks) == len(workflow_2.tasks))
145145
# Test the task graph topology
146-
assert (networkx.is_isomorphic(workflow_1, workflow_2))
146+
147+
# Slow (too slow for some DAGs) isomorphic check with networkx
148+
# assert (networkx.is_isomorphic(workflow_1, workflow_2))
149+
150+
# Fast isomorphic check using igraph
151+
import igraph as ig
152+
153+
def nx_to_igraph(G):
154+
g = ig.Graph(directed=True)
155+
g.add_vertices(list(G.nodes()))
156+
g.add_edges(list(G.edges()))
157+
return g
158+
159+
g1 = nx_to_igraph(workflow_1)
160+
g2 = nx_to_igraph(workflow_2)
161+
162+
assert(g1.isomorphic(g2))
163+
147164
# Test the total file size sum
148165
workflow1_input_bytes, workflow2_input_bytes = 0, 0
149166
workflow1_output_bytes, workflow2_output_bytes = 0, 0
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# docker build --platform amd64 -t wfcommons-dev-snakemake -f Dockerfile.snakemake .
2+
# docker run -it --rm -v `pwd`:/home/wfcommons wfcommons-dev-snakemake /bin/bash
3+
4+
FROM amd64/ubuntu:noble
5+
6+
LABEL org.containers.image.authors="henric@hawaii.edu"
7+
8+
# update repositories
9+
RUN apt-get update
10+
11+
# set timezone
12+
RUN echo "America/Los_Angeles" > /etc/timezone && export DEBIAN_FRONTEND=noninteractive && apt-get install -y tzdata
13+
14+
# install useful stuff
15+
RUN apt-get -y install pkg-config
16+
RUN apt-get -y install git
17+
RUN apt-get -y install wget
18+
RUN apt-get -y install curl
19+
RUN apt-get -y install make
20+
RUN apt-get -y install cmake
21+
RUN apt-get -y install cmake-data
22+
RUN apt-get -y install sudo
23+
RUN apt-get -y install vim --fix-missing
24+
RUN apt-get -y install gcc
25+
RUN apt-get -y install gcc-multilib
26+
RUN apt-get -y install graphviz libgraphviz-dev
27+
28+
29+
# Python stuff
30+
RUN apt-get -y install python3 python3-pip
31+
RUN update-alternatives --install /usr/bin/python python /usr/bin/python3 1
32+
RUN python3 -m pip install --break-system-packages pathos pandas filelock
33+
RUN python3 -m pip install --break-system-packages networkx scipy matplotlib pygraphviz
34+
RUN python3 -m pip install --break-system-packages pyyaml jsonschema requests
35+
RUN python3 -m pip install --break-system-packages --upgrade setuptools
36+
37+
# Stress-ng
38+
RUN apt-get -y install stress-ng
39+
40+
# Add wfcommons user
41+
RUN useradd -ms /bin/bash wfcommons
42+
RUN adduser wfcommons sudo
43+
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
44+
ENV PATH="$PATH:/home/wfcommons/.local/bin/"
45+
46+
USER wfcommons
47+
WORKDIR /home/wfcommons
48+
# Making this directory world rwx to facilitate testing
49+
RUN chmod -R 777 /home/wfcommons
50+
51+
52+
# Install Pixi
53+
RUN wget -qO- https://pixi.sh/install.sh | sh
54+
ENV PATH="$PATH:/home/wfcommons/.pixi/bin"
55+
56+
# Install snakemake
57+
RUN pixi global install snakemake conda -c conda-forge -c bioconda
58+
RUN pixi global install snakedeploy -c conda-forge -c bioconda
59+
RUN ~/.pixi/envs/snakemake/bin/python -m ensurepip && \
60+
~/.pixi/envs/snakemake/bin/python -m pip install snakemake-logger-plugin-snkmt
61+

tests/translators_loggers/test_translators_loggers.py

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
from tests.test_helpers import _shutdown_docker_container_and_remove_image
2525
from tests.test_helpers import _compare_workflows
2626

27-
from wfcommons import BlastRecipe
27+
from wfcommons import BlastRecipe, EpigenomicsRecipe, BwaRecipe, CyclesRecipe, GenomeRecipe, MontageRecipe, \
28+
RnaseqRecipe, SeismologyRecipe, SoykbRecipe, SrasearchRecipe
2829
from wfcommons.common import Workflow, Task
2930
from wfcommons.wfbench import WorkflowBenchmark
3031
from wfcommons.wfbench import DaskTranslator
@@ -34,6 +35,7 @@
3435
from wfcommons.wfbench import BashTranslator
3536
from wfcommons.wfbench import TaskVineTranslator
3637
from wfcommons.wfbench import MakeflowTranslator
38+
from wfcommons.wfbench import SnakemakeTranslator
3739
from wfcommons.wfbench import CWLTranslator
3840
from wfcommons.wfbench import StreamflowTranslator
3941
from wfcommons.wfbench import PegasusTranslator
@@ -43,16 +45,35 @@
4345
from wfcommons.wfinstances.logs import TaskVineLogsParser
4446
from wfcommons.wfinstances.logs import MakeflowLogsParser
4547
from wfcommons.wfinstances.logs import ROCrateLogsParser
48+
from wfcommons.wfinstances.logs import SnakemakeLogsParser
4649

4750

4851
def _create_workflow_benchmark() -> (WorkflowBenchmark, int):
4952
# Create a workflow benchmark object to generate specifications based on a recipe (in /tmp/, whatever)
5053
desired_num_tasks = 45
51-
benchmark_full_path = "/tmp/blast-benchmark-{desired_num_tasks}.json"
54+
benchmark_full_path = f"/tmp/blast-benchmark-{desired_num_tasks}.json"
55+
# benchmark_full_path = f"/tmp/epigenomics-benchmark-{desired_num_tasks}.json"
56+
# benchmark_full_path = f"/tmp/bwa-benchmark-{desired_num_tasks}.json"
57+
# benchmark_full_path = f"/tmp/cycles-benchmark-{desired_num_tasks}.json"
58+
# benchmark_full_path = f"/tmp/genome-benchmark-{desired_num_tasks}.json"
59+
# benchmark_full_path = f"/tmp/montage-benchmark-{desired_num_tasks}.json"
60+
# benchmark_full_path = f"/tmp/rnaseq-benchmark-{desired_num_tasks}.json"
61+
# benchmark_full_path = f"/tmp/seismology-benchmark-{desired_num_tasks}.json"
62+
# benchmark_full_path = f"/tmp/soykb-benchmark-{desired_num_tasks}.json"
63+
# benchmark_full_path = f"/tmp/srasearch-benchmark-{desired_num_tasks}.json"
5264
shutil.rmtree(benchmark_full_path, ignore_errors=True)
5365
benchmark = WorkflowBenchmark(recipe=BlastRecipe, num_tasks=desired_num_tasks)
66+
# benchmark = WorkflowBenchmark(recipe=EpigenomicsRecipe, num_tasks=desired_num_tasks)
67+
# benchmark = WorkflowBenchmark(recipe=BwaRecipe, num_tasks=desired_num_tasks)
68+
# benchmark = WorkflowBenchmark(recipe=CyclesRecipe, num_tasks=desired_num_tasks)
69+
# benchmark = WorkflowBenchmark(recipe=GenomeRecipe, num_tasks=desired_num_tasks)
70+
# benchmark = WorkflowBenchmark(recipe=MontageRecipe, num_tasks=desired_num_tasks)
71+
# benchmark = WorkflowBenchmark(recipe=RnaseqRecipe, num_tasks=desired_num_tasks)
72+
# benchmark = WorkflowBenchmark(recipe=SeismologyRecipe, num_tasks=desired_num_tasks)
73+
# benchmark = WorkflowBenchmark(recipe=SoykbRecipe, num_tasks=desired_num_tasks)
74+
# benchmark = WorkflowBenchmark(recipe=SrasearchRecipe, num_tasks=desired_num_tasks)
5475
benchmark.create_benchmark(pathlib.Path("/tmp/"), cpu_work=10, data=10, percent_cpu=0.6)
55-
with open(f"/tmp/blast-benchmark-{desired_num_tasks}.json", "r") as f:
76+
with open(benchmark_full_path, "r") as f:
5677
generated_json = json.load(f)
5778
num_tasks = len(generated_json["workflow"]["specification"]["tasks"])
5879
return benchmark, num_tasks
@@ -119,6 +140,7 @@ def _additional_setup_swiftt(container):
119140
"bash": noop,
120141
"taskvine": _additional_setup_taskvine,
121142
"makeflow": noop,
143+
"snakemake": noop,
122144
"cwl": noop,
123145
"streamflow": noop,
124146
"pegasus": _additional_setup_pegasus,
@@ -199,6 +221,15 @@ def run_workflow_makeflow(container, num_tasks, str_dirpath):
199221
num_completed_jobs = len(re.findall(r'job \d+ completed', output.decode()))
200222
assert (num_completed_jobs == num_tasks)
201223

224+
def run_workflow_snakemake(container, num_tasks, str_dirpath):
225+
# Run the workflow (with full logging)
226+
exit_code, output = container.exec_run(cmd=["bash", "-c", "snakemake -s ./workflow.smk --cores 1 --logger snkmt --logger-snkmt-db ./snkmt.sqlite"],
227+
user="wfcommons", stdout=True, stderr=True)
228+
# Check sanity
229+
assert (exit_code == 0)
230+
num_completed_jobs = len(re.findall(r'Finished jobid: \d+', output.decode()))
231+
assert (num_completed_jobs - 1 == num_tasks) # Discounting the "all_tasks" rule
232+
202233
def run_workflow_cwl(container, num_tasks, str_dirpath):
203234
# Run the workflow!
204235
# Note that the input file is hardcoded and Blast-specific
@@ -214,7 +245,6 @@ def run_workflow_cwl(container, num_tasks, str_dirpath):
214245

215246
def run_workflow_streamflow(container, num_tasks, str_dirpath):
216247
# Run the workflow!
217-
# Note that the input file is hardcoded and Blast-specific
218248
exit_code, output = container.exec_run(cmd="streamflow run ./streamflow.yml",
219249
user="wfcommons", stdout=True, stderr=True)
220250
# Check sanity
@@ -264,6 +294,7 @@ def run_workflow_swiftt(container, num_tasks, str_dirpath):
264294
"bash": run_workflow_bash,
265295
"taskvine": run_workflow_taskvine,
266296
"makeflow": run_workflow_makeflow,
297+
"snakemake": run_workflow_snakemake,
267298
"cwl": run_workflow_cwl,
268299
"streamflow": run_workflow_streamflow,
269300
"pegasus": run_workflow_pegasus,
@@ -279,6 +310,7 @@ def run_workflow_swiftt(container, num_tasks, str_dirpath):
279310
"bash": BashTranslator,
280311
"taskvine": TaskVineTranslator,
281312
"makeflow": MakeflowTranslator,
313+
"snakemake": SnakemakeTranslator,
282314
"cwl": CWLTranslator,
283315
"streamflow": StreamflowTranslator,
284316
"pegasus": PegasusTranslator,
@@ -291,18 +323,19 @@ class TestTranslators:
291323
@pytest.mark.parametrize(
292324
"backend",
293325
[
294-
"swiftt",
295-
"dask",
296-
"parsl",
297-
"nextflow",
298-
"nextflow_subworkflow",
299-
"airflow",
300-
"bash",
301-
"taskvine",
302-
"makeflow",
303-
"cwl",
304-
"streamflow",
305-
"pegasus",
326+
"swiftt",
327+
"dask",
328+
"parsl",
329+
"nextflow",
330+
"nextflow_subworkflow",
331+
"airflow",
332+
"bash",
333+
"taskvine",
334+
"makeflow",
335+
"snakemake",
336+
"cwl",
337+
"streamflow",
338+
"pegasus",
306339
])
307340
@pytest.mark.unit
308341
# @pytest.mark.skip(reason="tmp")
@@ -359,12 +392,13 @@ def test_translator(self, backend) -> None:
359392
steps_to_ignore=["main.cwl#compile_output_files", "main.cwl#compile_log_files"],
360393
file_extensions_to_ignore=[".out", ".err"],
361394
instruments_to_ignore=["shell.cwl"])
395+
elif backend == "snakemake":
396+
parser = SnakemakeLogsParser(dirpath, snkmt_db=dirpath / "snkmt.sqlite", rules_to_ignore=["all_wfbench_tasks"])
362397

363398
if parser is not None:
364399
sys.stderr.write(f"[{backend}] Parsing the logs...\n")
365400
reconstructed_workflow : Workflow = parser.build_workflow(f"reconstructed_workflow_{backend}")
366401
reconstructed_workflow.write_json(pathlib.Path("/tmp/reconstructed_workflow.json"))
367-
368402
original_workflow : Workflow = benchmark.workflow
369403

370404
_compare_workflows(original_workflow, reconstructed_workflow)

wfcommons/wfbench/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
SwiftTTranslator,
1919
TaskVineTranslator,
2020
MakeflowTranslator,
21+
SnakemakeTranslator,
2122
CWLTranslator,
2223
StreamflowTranslator,
2324
PyCompssTranslator)

wfcommons/wfbench/translator/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@
2020
from .swift_t import SwiftTTranslator
2121
from .taskvine import TaskVineTranslator
2222
from .makeflow import MakeflowTranslator
23+
from .snakemake import SnakemakeTranslator

wfcommons/wfbench/translator/airflow.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ def __init__(self,
4141
self.script = f"""
4242
from __future__ import annotations
4343
44-
import os
45-
from datetime import datetime
4644
from airflow.models.dag import DAG
4745
from airflow.operators.bash import BashOperator
4846
@@ -64,9 +62,6 @@ def translate(self, output_folder: pathlib.Path, name: Optional[str] = None) ->
6462
with DAG(
6563
"{name}",
6664
description="airflow translation of a wfcommons instance",
67-
schedule="0 0 * * *",
68-
start_date=datetime(2021, 1, 1),
69-
catchup=False,
7065
tags=["wfcommons"],
7166
) as dag:
7267
"""
@@ -77,12 +72,10 @@ def translate(self, output_folder: pathlib.Path, name: Optional[str] = None) ->
7772
self.script += f"""
7873
{self._sanitize_varname(task.task_id)} = BashOperator(
7974
task_id="{task.task_id}",
80-
depends_on_past=False,
8175
bash_command='{self.task_commands[task.task_id]}',
82-
env={{"AIRFLOW_HOME": os.environ["AIRFLOW_HOME"]}},
83-
retries=3,
8476
)
8577
"""
78+
8679
for task in self.tasks.values():
8780
# Comma-separated list of the task's parents
8881
parents = ", ".join(map(self._sanitize_varname, self.task_parents[task.task_id]))

0 commit comments

Comments
 (0)