Skip to content

Workflows

A PERSEUS workflow automates an operation on a project: when something happens (a trigger), if the project is in a certain shape (conditions), do this (actions).

Workflows are created and managed centrally in the workflow registry. A project only reacts to a workflow once it is subscribed to it, which is managed from the project details view. The same workflow can be subscribed to by many projects at once.

A trigger decides when a workflow should be checked. A workflow can have several triggers, and they are combined with OR — the workflow is checked whenever any one of them fires.

  • Time trigger: a cron schedule expression with five fields (minute, hour, day of month, month, day of week, where 0 is Sunday), for example 0 3 * * * for daily at 3 AM UTC. *, */n (every n units) and comma-separated lists are supported. Ranges (e.g. 1-5), named months or weekdays, six-field schedules, and shorthand macros such as @daily are not supported — use the equivalent five-field expression instead.
  • Event trigger: fires when a named event occurs elsewhere in PERSEUS, for example when a project changes state. If the event is tied to a specific project, only that project’s subscription reacts; otherwise every subscribed project does.

A condition is a short, python-like expression that must evaluate to True for the workflow’s actions to run, for example:

end - now < timedelta(days=30)

It is never executed with Python’s eval(). Instead it is parsed once when you save it and evaluated in a restricted sandbox that only ever sees the names and functions listed below — there are no imports, no builtins and no way to reach outside of it.

A workflow with no conditions always runs once triggered.

NameTypeMeaning
any Project attribute—e.g. title, abbreviation, start, end, member_ids, granted_resources
projectProjectthe project itself, if you prefer project.title over title
current_stateslist[str]the currently active states of the project
nowdatetimethe moment the workflow is being checked
JobStateenumso you can write JobState.RUNNING

Besides abs, all, any, datetime, float, int, len, max, min, round, sorted, str, sum and timedelta, the following are available:

FunctionReturns
matches(value, pattern)True if the regular expression pattern is found in str(value)
get_item(array, criteria)the first item in array matching every key in criteria, or None
get_jobs(...)the project’s jobs, optionally filtered by cluster, partition, state, priority, or submitted/started/ended time ranges
get_usage_total(resource_id, ...)total usage of a resource across all phases
get_usage_current_phase(resource_id, ...)usage of a resource within the current phase only
get_used_contingent_total(resource_id, ...)same as get_usage_total, but as a share of the granted contingent
get_used_contingent_current_phase(resource_id, ...)same as get_used_contingent_total, restricted to the current phase
"'ComputeProjectRunning' in current_states"
"end is not None and end - now < timedelta(days=30)"
"matches(abbreviation, '^HPC-')"
"len(get_jobs(state=JobState.RUNNING)) > 100"
"get_usage_current_phase('core-h') > 0.9 * 1_000_000"

Each condition (except the last) has an and/or operator that connects it to the next condition. The list is combined strictly from left to right, without normal operator precedence:

[a (or), b (and), c] → (a or b) and c

Put cheap checks first and expensive ones (such as get_jobs or the usage functions) last — evaluation short-circuits, so a condition that can no longer change the result is never evaluated.

Actions run in the order they are configured. If one fails, the remaining actions for that run are skipped. Currently available actions:

  • Send email: sends a formatted email to the project’s principal investigator (PI), person of contact (PC), a specific person, or a literal email address. The subject, content, and optional call-to-action button text/URL may contain placeholders that are filled in for the project at the moment the email is sent.
  • Trigger event: emits a named event, optionally with a fixed payload, which other workflows can react to via an event trigger. The current project and workflow are always added to the payload automatically.
Additional action types (such as changing a project’s state or opening a support ticket) are planned for future versions.

PERSEUS checks all workflows once every minute rather than reacting instantly. For each project subscribed to a workflow whose trigger fired, the following is checked, from cheapest to most expensive, before any conditions are evaluated:

  1. Is the subscription enabled?
  2. Are we within the subscription’s (or, if unset, the project’s) start/end window?
  3. Does the trigger that fired apply to this project?
  4. Has the workflow’s maximum number of repetitions already been reached for this project? This counter is cumulative for the project and is not reset by disabling and re-enabling the subscription.
  5. Has the configured cooldown since the last execution already elapsed? The cooldown always takes precedence over the trigger — even if the trigger fires again, the workflow will not run again until the cooldown has passed.

Only once every one of these passes are the workflow’s conditions evaluated. If they hold, the actions run in order and the outcome is recorded, which is what drives the execution history shown in the project details view.