|
|
(50 intermediate revisions by 2 users not shown) |
Line 1: |
Line 1: |
− | == Step 0: rename __call__ to execute ==
| + | '''Revised on:''' {{REVISIONMONTH1}}/{{REVISIONDAY}}/{{REVISIONYEAR}} by {{REVISIONUSER}} |
| | | |
− | Just do that, __call__ looks too magical.
| + | The page was moved to developers documentation: http://docs.openstack.org/developer/taskflow/arguments_and_results.html |
− | | |
− | == Required task arguments ==
| |
− | | |
− | Required arguments MUST be passed to task when it is executed, as
| |
− | <code>execute</code> method arguments.
| |
− | | |
− | Required arguments list should be inferred from <code>execute</code> method
| |
− | signature, like we already do for <code>@task</code> decorator.
| |
− | | |
− | We also need provide a way to override it, to make adaptors like FunctorTask
| |
− | possible.
| |
− | | |
− | == Task results ==
| |
− | | |
− | Task result is what task returns from <code>execute</code> method. It is saved
| |
− | in persistence layer with task details and is passed to <code>revert</code> method when/if
| |
− | task is reverted.
| |
− | | |
− | To map result of already run task to task arguments, we must name task results
| |
− | This names are used as a key to storage ('memory' in TaskMachine terms) access
| |
− | | |
− | But we can't infer name from task signature, result naming should be explicit.
| |
− | To make solution more symmetric to inferring names from signature, we propose
| |
− | to use decorator on execute method for that.
| |
− | | |
− | For example:
| |
− | | |
− | class MyTask(task.Task):
| |
− | @decorators.returns('foo', 'bar', 'baz'):
| |
− | def execute(self, context, spam, eggs):
| |
− | return 4, 8, 15 # 23 42
| |
− | | |
− | This required arguments are <code>context</code>, <code>spam</code> and
| |
− | <code>eggs</code>; it returns three values: <code>foo</code>, <code>bar</code>
| |
− | and <code>baz</code>. From it's implementation it is easy to see that
| |
− | <code>foo</code> will be equal to 4, <code>bar</code> to 8 and <code>baz</code>
| |
− | to 15.
| |
− | | |
− | Again, we need provide a way to override it, to make adaptors like FunctorTask
| |
− | possible.
| |
− | | |
− | == Note ==
| |
− | | |
− | Farther examples specify flows using blocks/patterns from [[TaskFlow/Patterns and Engines]].
| |
− | | |
− | == Saving result with different name ==
| |
− | | |
− | This is sometimes convenient to save task result with different name while
| |
− | defining flow. For that purpose, we propose adding <code>save_as</code>
| |
− | non-required parameter for task block.
| |
− | | |
− | For example, to spawn a VM, you might implement a simple task
| |
− | | |
− | class SpawnVMTask(taskTask):
| |
− | @decorator.returns('vm_id')
| |
− | def execute(self, context, vm_name, vm_params):
| |
− | servers = get_server_manager(context)
| |
− | return servers.create(name=vm_name, **vm_params).id
| |
− | | |
− | Then, to create two servers with same parameters in parallel, you can use
| |
− | parallel flow; to save VM ids with different names, e.g. <code>'vm_id_one'</code> and <code>'vm_id_two'</code>,
| |
− | add <code>save_as</code> parameters like this:
| |
− | | |
− | | |
− | blocks.ParallelFlow().add(
| |
− | blocks.Task(SpawnVMTask, save_as='vm_id_one')
| |
− | blocks.Task(SpawnVMTask, save_as='vm_id_two')
| |
− | )
| |
− | | |
− | == Rebinding arguments ==
| |
− | | |
− | There are cases when value you want to pass to task is stored with name other
| |
− | then corresponding task argument. For that it is proposed to add <code>rebind_args</code>
| |
− | task block parameter.
| |
− | | |
− | There are two possible way of using it. First is to pass dictionary that maps
| |
− | task argument name to name of saved value. For example, if you saved vm name
| |
− | with 'name' key, you can spawn vm with such name like this:
| |
− | | |
− | blocks.Task(SpawnVMTask, rebind_args={'vm_name': 'name'})
| |
− | | |
− | Second, you can pass a tuple or list of argument names, and values with that
| |
− | names are passed to task. The length of tuple or list should not be less then
| |
− | number of task required parameters. For example, you can achieve same effect as
| |
− | the previous example with
| |
− | | |
− | blocks.Task(SpawnVMTask, rebind_args=('context', 'name', 'vm_params'))
| |
− | | |
− | which is equivalent to more elaborate
| |
− | | |
− | blocks.Task(SpawnVMTask,
| |
− | rebind_args=dict(context='context',
| |
− | vm_name='name',
| |
− | vm_params='vm_params'))
| |
− | | |
− | == Optional task arguments ==
| |
− | | |
− | Task gets optional arguments as keyword arguments to <code>execute</code>.
| |
− | Task authores may use <code>**kwargs</code> syntax or add optional parameters
| |
− | as positional parameters with default values.
| |
− | | |
− | There is no need to specify what optional arguments task accepts, also it
| |
− | definitely worth to document them. Which optional arguments are passed to task
| |
− | is specified in flow definition, in the same way as it is done
| |
− | for argument rebinding: add extra arguments to list or dict and these arguments
| |
− | will be fetched from storage and passed to <code>execute</code> as keyword
| |
− | arguments.
| |
− | | |
− | TODO(imelnikov): example here.
| |