NOTE: DSL described in this document might slightly change within a short period of time (2-3 weeks) and should be now considered experimental. Mistral team is now actively working on stabilization.
Current document fully describes Domain Specific Language (DSL) version 2 of Mistral Workflow Service. Since version 1 issued in May 2014 Mistral team completely reworked the language pursuing with the goal in mind to make it easier to understand while more consistent and flexible.
Unlike Mistral DSL v1 this second version of DSL assumes that all entities that Mistral works with like workflows, actions and triggers are completely independent in terms of how they’re referenced and accessed through API (and also Python Client API and CLI). Workbooks, the entity that can combine combine workflows/actions/triggers still exist in the language but only for namespacing and convenience purposes. See Workbooks section for more details.
All DSL consists of the following main object(entity) types that will be described in details next:
Mistral DSL is fully based on YAML and knowledge of YAML is a plus for better understanding of the material in this specification. It also takes advantage of YAQL query language to define expressions in workflow, action and trigger definitions.
Workflow is the main building block of Mistral DSL, the reason why the project exists. Workflow represents a process that can be described in a various number of ways and that can do some job interesting to the end user. Each workflow consists of tasks (at least one) describing what exact steps should be made during workflow execution.
Mistral DSL v2 introduces different workflow types and the structure of each workflow type varies according to its semantics. Currently, Mistral provides two workflow types:
See corresponding sections for details.
Task is what a workflow consists of. It defines a specific computational step in the workflow. Each task can optionally take input data and produce output. In Mistral DSL v2 task can be associated with an action or with calling a workflow. In the example below there are two tasks of different types:
action_based_task:
action: std.http url='openstack.org'
workflow_based_task:
workflow: backup_vm_workflow vm_id={$.vm_id}
Actions will be explained below in a individual paragraph but looking ahead it’s worth saying that Mistral provides a lot of actions out of the box (including actions for most of the core OpenStack services) and it’s also easy to plug new actions into Mistral.
All Mistral tasks regardless of workflow type have the following common attributes:
Any Mistral task regardless of what its workflow type can optionally have configured policies.
YAML example
‘wait-before’
Defines a delay in seconds that Mistral Engine should wait before starting a task.
‘wait-after’
Defines a delay in seconds that Mistral Engine should wait after a task has completed before starting next tasks defined in ‘on-success’, ‘on-error’ or ‘on-complete’.
‘pause-before’
The Mistral Engine will pause the workflow and its task with the ‘pause-before’ policy before executing it. The workflow and task will be paused until a resume signal is received. This policy accepts a YAQL expression which will cause the policy to be applied only if the expression evaluates to ‘True’.
‘timeout’
Defines a period of time in seconds after which a task will be failed automatically by engine if hasn’t completed.
‘retry’
Defines a pattern how task should be repeated in case of an error.
When describing a workflow task it’s possible to specify its input parameters in two ways:
Full syntax:
Simplified syntax:
The same rules apply to tasks associated with workflows.
Full syntax:
Simplified syntax:
Note: It’s also possible to merge these two approaches and specify a part of parameters using simplified key-value pairs syntax and using keyword ‘input’. In this case all the parameters will be effectively merged. If the same parameter is specified in both ways then the one under ‘input’ keyword takes precedence.
Direct workflow consists of tasks combined in a graph where every next task starts after another one depending on produced result. So direct workflow has a notion of transition. Direct workflow is considered to be completed if there aren’t any transitions left that could be used to jump to next tasks.
Figure 1. Mistral Direct Workflow.
Task transitions can be determined by success/error/completeness of the previous tasks and also by additional YAQL guard expressions that can access any data produced by upstream tasks. So in the example above task ‘create_vm’ could also have a YAQL expression on transition to task ‘send_success_email’ as follows:
And this would tell Mistral to run ‘send_success_email’ task only if ‘vm_id’ variable published by task ‘create_vm’ is not empty. YAQL expressions can also be applied to ‘on-error’ and ‘on-complete’.
In reverse workflow all relationships in workflow task graph are dependencies. In order to run this type of workflow we need to specify a task that needs to be completed, it can be conventionally called ‘target task’. When Mistral Engine starts a workflow it recursively identifies all the dependencies that need to be completed first.
Figure 2. Mistral Reverse Workflow.
Figure 2 explains how reverse workflow works. In the example, task T1 is chosen a target task. So when the workflow starts Mistral will run only tasks T7, T8, T5, T6, T2 and T1 in the specified order (starting from tasks that have no dependencies). Tasks T3 and T4 won’t be a part of this workflow because there’s no route in the directed graph from T1 to T3 or T4.
Action defines what exactly needs to be done when task starts. Action is similar to a regular function in general purpose programming language like Python. It has a name and parameters. Mistral distinguishes ‘system actions’ and ‘Ad-hoc actions’.
System actions are provided by Mistral out of the box and can be used by anyone. It is also possible to add system actions for specific Mistral installation via a special plugin mechanism. Currently, built-in system actions are:
Sends an HTTP request.
Input parameters:
This actions works just like ‘std.http’ with the only exception: when sending a request it inserts the following HTTP headers:
Using this action makes it possible to do any work in asynchronous manner triggered via HTTP protocol. That means that Mistral can send a request using ‘std.mistral_http’ and then any time later whatever system that received this request can notify Mistral back (using its public API) with the result of this action. Header Mistral-Task-Id is required for this operation because it is used a key to find corresponding task in Mistral to attach the result to.
Sends an email message via SMTP protocol.
The syntax of ‘std.emal’ action is pretty verbose. However, it can be significantly simplified using Ad-hoc actions. More about them below.
Runs Secure Shell command.
Input parameters:
Simple action mostly needed for testing purposes that returns a predefined result.
Input parameters:
Ad-hoc action is a special type of action that can be created by user. Ad-hoc action is always created as a wrapper around any other existing system action and its main goal is to simplify using same actions many times with similar pattern.
Note: Nested ad-hoc actions currently are not supported (i.e. ad-hoc action around another ad-hoc action).
Once this action is uploaded to Mistral any workflow will be able to use it as follows:
NOTE: Triggers are not yet implemented as part of version 0.1, they will go into in one of the next builds, likely 0.2
Using triggers it is possible to run workflows according to specific rules: periodically setting a cron (http://en.wikipedia.org/wiki/Cron) pattern or on external events like ceilometer alarm.
Below are two options picturing what Mistral team is currently discussing as a candidate for implementation:
Option 1:
Option 2:
If you are interested in this functionality you can participate in mailing list openstack-dev@lists.openstack.org.
As mentioned before, workbooks still exist in Mistral DSL version 2 but purely for convenience. Using workbooks users can combine multiple entities of any type (workflows, actions and triggers) into one document and upload to Mistral service. When uploading a workbook Mistral will parse it and save its workflows, actions and triggers as independent objects which will be accessible via their own API endpoints (/workflows, /actions and /triggers/). Once it’s done the workbook comes out of the game. User can just start workflows and use references to workflows/actions/triggers as if they were uploaded without workbook in the first place. However, if we want to modify these individual objects we can modify the same workbook definition and re-upload it to Mistral (or, of course, we can do it independently).
One thing that’s worth noting is that when using a workbook Mistral uses its name as a prefix for generating final names of workflows, actions and triggers included into the workbook. To illustrate this principle let’s take a look at the figure below.
Note: Even though names of objects inside workbooks change upon uploading Mistral allows referencing between those objects using local names declared in the original workbook.