Add an experiment type#
This guide walks through implementing a new experiment type plugin for Kiso (for example, a custom workflow engine).
Read How Kiso extensions work first. Refer to the Experiment type interface reference for complete method signatures.
Prerequisites#
Familiarity with Python dataclasses and EnOSlib
An existing Kiso development setup:
pip install -e ".[all]" && pre-commit install
Step 1 — Create the plugin subpackage#
src/kiso_myworkflow/
__init__.py
runner.py
configuration.py
schema.py
Step 2 — Define the configuration dataclass#
# configuration.py
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class MyWorkflowConfiguration:
kind: str = "myworkflow"
name: str
main: str # Script to execute
submit_node_labels: list[str]
description: Optional[str] = None
timeout: int = 600
variables: dict = field(default_factory=dict)
Step 3 — Define the JSON schema#
# schema.py
schema = {
"type": "object",
"properties": {
"kind": {"type": "string", "const": "myworkflow"},
"name": {"type": "string"},
"main": {"type": "string"},
"submit_node_labels": {
"type": "array",
"items": {"type": "string"},
"minItems": 1,
},
"description": {"type": "string"},
"timeout": {"type": "integer", "minimum": 1},
"variables": {
"type": "object",
"additionalProperties": {
"oneOf": [
{"type": "string"},
{"type": "number"},
]
},
},
},
"required": ["kind", "name", "main", "submit_node_labels"],
"additionalProperties": False,
}
Step 4 — Implement the runner class#
# runner.py
import logging
from pathlib import Path
from enoslib.api import run_command
from kiso import constants as const, edge, utils
from kiso_myworkflow.configuration import MyWorkflowConfiguration
from kiso_myworkflow.schema import SCHEMA
log = logging.getLogger(__name__)
class MyWorkflowRunner:
kind: str = "myworkflow"
schema: dict = SCHEMA
config_type: type = MyWorkflowConfiguration
def __init__(self, config: MyWorkflowConfiguration):
self.config = config
def check(self, label_to_machines: dict) -> None:
"""Validate labels and configuration."""
for label in self.config.submit_node_labels:
if label not in label_to_machines:
raise ValueError(
f"Experiment '{self.config.name}' references label '{label}' "
"which does not exist in sites"
)
def __call__(self, env) -> None:
"""Execute the workflow and wait for completion."""
log.info("Running experiment: %s", self.config.name)
labels = env["labels"]
_labels = utils.resolve_labels(labels, self.config.labels)
vms, containers = utils.split_labels(_labels, labels)
results = []
if vms:
# Steps to run the experiment on VMS
# Either as an Ansible playbook YAML file or
# as Python code using utils.actions (wrapper over EnOSlib' actions)
with utils.actions(
roles=vms,
run_as=const.KISO_USER,
on_error_continue=True,
strategy="free",
) as p:
p.copy(
src=str(src),
dest=str(dst),
mode="preserve",
task_name=f"Copy input file {instance}",
)
p.shell(f"rm -rf tempfile", chdir=self.remote_wd)
results.extend(p.results)
if containers:
# Steps to run the experiment on containers
for container in containers:
results.append(
edge.run_script(
container,
Path(__file__).parent / "runner.sh",
"--no-dry-run",
timeout=-1,
)
)
# Render the results
See also
See Kiso API reference to reuse code to upload files, download files, andrun commands, request public IPs, etc.
Step 5 — Register the entry point#
In pyproject.toml:
[project.entry-points."kiso.experiment"]
myworkflow = "kiso_myworkflow.runner:MyWorkflowRunner"
Reinstall:
pip install -e ".[all]"
Step 7 — Verify the plugin loads#
kiso check experiment.yml
With a config that uses experiments[] with kind: myworkflow, the validator should accept it. An invalid config should be rejected.
Step 8 — Write tests#
Add tests in tests/experiments/ following the existing patterns in tests/experiments/shell/ and tests/experiments/pegasus/. Run:
pytest tests/
See also#
Experiment type interface reference — complete method signatures
How Kiso extensions work — extension model overview
Contributing — submitting your plugin to the project