# Working with workflows ## Creating a workflow To create a new workflow, create a tag with the same name, and then the workflow. ```shell $ snactor workflow new Example ``` This procedure creates the Example workflow boilerplate: ```python from leapp.workflows import Workflow from leapp.workflows.phases import Phase from leapp.workflows.flags import Flags from leapp.workflows.tagfilters import TagFilter from leapp.workflows.policies import Policies from leapp.tags import ExampleWorkflowTag class ExampleWorkflow(Workflow): name = 'Example' tag = ExampleWorkflowTag short_name = 'example' description = '''No description has been provided for the Example workflow.''' # Template for a phase definition. The order in which the phase classes are defined # within the Workflow class represents the execution order. # # class PhaseName(Phase): # name = 'phase_name' # filter = TagFilter(PhaseTag) # policies = Policies(Policies.Errors.FailPhase, # Policies.Retry.Phase) # flags = Flags() ``` ## Defining workflow phases To add a phase, define a new subclass within the workflow deriving from the Phase class. We will create a phase called ScanPhase, which is supposed to be handling all actors that are defining the ScanTag for the phase, and the ExampleWorkflowTag for the workflow. Phases have policies that control the execution of the workflow. These policies can control the behavior in case of errors that are reported by actors. Additionally, the retry behavior can be specified. The retry behavior allows to specify how to recover from failing workflow executions without having to rerun the whole process or to disable the retry ability entirely. In this scenario, we set the policy to fail the whole phase, but let all actors run, even if one of the actors fails. And for the retry policy, we specify to restart the whole phase from the beginning. The definition of the ScanPhase class: (Note: ScanTag has to be imported) ```python class ScanPhase(Phase): name = 'scan phase' filter = TagFilter(ScanTag) policies = Policies(Policies.Errors.FailPhase, Policies.Retry.Phase) flags = Flags() ``` Now, we will define an imaginary reports phase that would process the data produced by the ScanPhase and create one or more reports. For this, the ReportsTag is used. This time, we will make the phase fail immediately, and stop the workflow execution once one of the actors fails. And we disallow the retry by disabling it, which means that the phase cannot be recovered from. This implies that the workflow has to be restarted from the very beginning. The definition of the ReportsPhase class: (Note: ReportsTag has to be imported) ```python class ReportsPhase(Phase): name = 'reports phase' filter = TagFilter(ReportsTag) policies = Policies(Policies.Errors.FailImmediately, Policies.Retry.Disabled) flags = Flags() ``` The whole example workflow: ```python from leapp.workflows import Workflow from leapp.workflows.phases import Phase from leapp.workflows.flags import Flags from leapp.workflows.tagfilters import TagFilter from leapp.workflows.policies import Policies from leapp.tags import ExampleWorkflowTag, ScanTag, ReportsTag class ExampleWorkflow(Workflow): name = 'Example' tag = ExampleWorkflowTag short_name = 'example' description = '''No description has been provided for the Example workflow.''' class ScanPhase(Phase): name = 'scan phase' filter = TagFilter(ScanTag) policies = Policies(Policies.Errors.FailPhase, Policies.Retry.Phase) flags = Flags() class ReportsPhase(Phase): name = 'reports phase' filter = TagFilter(ReportsTag) policies = Policies(Policies.Errors.FailImmediately, Policies.Retry.Disabled) flags = Flags() ``` ## Testing the workflow execution To test the execution of workflows, use the snactor tool. The snactor tool is run with the above workflow in the tutorial repository, which contains the HostnameScanner and IpResolver actors. ```shell $ snactor workflow run Example 2018-04-04 09:23:54.767 INFO PID: 38687 leapp: Logging has been initialized 2018-04-04 09:23:54.772 INFO PID: 38687 leapp.repository.tutorial: New repository 'tutorial' initialized at /home/evilissimo/devel/tutorial 2018-04-04 09:23:54.797 INFO PID: 38687 leapp.workflow: Starting workflow execution: Example - ID: c4615ed9-662b-49c6-8389-19f6128cdac5 2018-04-04 09:23:54.804 INFO PID: 38687 leapp.workflow: Starting phase scan phase 2018-04-04 09:23:54.805 INFO PID: 38687 leapp.workflow.scan phase: Starting stage Before of phase scan phase 2018-04-04 09:23:54.811 INFO PID: 38687 leapp.workflow.scan phase: Starting stage Main of phase scan phase 2018-04-04 09:23:54.813 INFO PID: 38687 leapp.workflow.scan phase: Executing actor hostname_scanner 2018-04-04 09:23:54.820 INFO PID: 38695 leapp.workflow.scan phase.hostname_scanner: Starting to scan for the hostname 2018-04-04 09:24:05.153 INFO PID: 38695 leapp.workflow.scan phase.hostname_scanner: Finished scanning for the hostname, found = actor-developer 2018-04-04 09:24:05.157 INFO PID: 38687 leapp.workflow.scan phase: Executing actor ip_resolver 2018-04-04 09:24:05.165 INFO PID: 38696 leapp.workflow.scan phase.ip_resolver: Starting to resolve hostnames 2018-04-04 09:24:10.325 INFO PID: 38687 leapp.workflow.scan phase: Starting stage After of phase scan phase 2018-04-04 09:24:10.328 INFO PID: 38687 leapp.workflow: Starting phase reports phase 2018-04-04 09:24:10.330 INFO PID: 38687 leapp.workflow.reports phase: Starting stage Before of phase reports phase 2018-04-04 09:24:10.332 INFO PID: 38687 leapp.workflow.reports phase: Starting stage Main of phase reports phase 2018-04-04 09:24:10.334 INFO PID: 38687 leapp.workflow.reports phase: Starting stage After of phase reports phase ``` ## Adding an actor to a workflow To have an actor added to a specific workflow phase, assign two tags: 1. The workflow tag In the Example workflow above this was the ExampleWorkflowTag. 2. The phase tag In case of the ScanPhase it is the ScanTag, in the Reports phase the ReportsTag. In the actor, the tags field is filled like this: ```python tags = (ExampleWorkflowTag, ScanTag) ``` To have an actor added to any workflow when a phase tag is used, add the `.Common` attribute of the tag: ```python tags = (ScanTag.Common,) ```