diff --git a/doc/source/cli/cli_usage_source_execution.rst b/doc/source/cli/cli_usage_source_execution.rst new file mode 100644 index 00000000..49d116f5 --- /dev/null +++ b/doc/source/cli/cli_usage_source_execution.rst @@ -0,0 +1,55 @@ +Replicating Workflows with Mistral +================================== + +The new command line switch '-s' will allow the operator to replicate / clone +an existing workflow execution based on its ID. Once id is given mistral will +create a new workflow execution based on the parameters of the first, which +will provide a simple approach to spawning a number of workflow executions +without having to specify inputs or parameters. Otherwise you can override +some of the parameters (e.g. some of the input variables) + +Basic Usage +----------- + +From the command line the operator will issue the following. The first step +would be to list the current executions, which is done with "execution-list". +The following step is to take the listed execution id and pass it to the source +execution switch "-s". + +.. code-block:: shell + + mistral execution-list + + mistral execution-create -s + +Once the workflow execution is selected and the replicate command used you +should see a newly created workflow execution based on an existing one with +a new execution id. + +.. code-block:: shell + + mistral execution-create -s 123e4567-e89b-12d3-a456-426655440000 + ++--------------------+---------------------------------------+ +| Field | Value | ++--------------------+---------------------------------------+ +| ID | 123e4567-e89b-12d3-a456-77046883182e | +| | | +| Workflow ID | 123e4567-e89b-12d3-a456-45411dfa33af | +| | | +| Workflow name | some.workflow.name.goes.here | +| | | +| Workflow namespace | | +| | | +| Description | | +| | | +| Task Execution ID | | +| | | +| State | RUNNING | +| | | +| State info | None | +| | | +| Created at | 2018-01-25 18:41:07 | +| | | +| Updated at | 2018-01-25 18:41:07 | ++--------------------+---------------------------------------+ diff --git a/doc/source/index.rst b/doc/source/index.rst index dad7824e..1c9383f5 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -14,6 +14,7 @@ Using mistralclient cli/cli_usage_with_openstack cli/cli_usage_with_keycloak cli/cli_usage_without_auth + cli/cli_usage_source_execution class_reference For information about using the mistral command-line client, see diff --git a/mistralclient/api/v2/executions.py b/mistralclient/api/v2/executions.py index 1e1bff46..08b3324e 100644 --- a/mistralclient/api/v2/executions.py +++ b/mistralclient/api/v2/executions.py @@ -31,18 +31,24 @@ class Execution(base.Resource): class ExecutionManager(base.ResourceManager): resource_class = Execution - def create(self, workflow_identifier, namespace='', workflow_input=None, - description='', **params): - self._ensure_not_empty(workflow_identifier=workflow_identifier) + def create(self, workflow_identifier='', namespace='', + workflow_input=None, description='', source_execution_id=None, + **params): + ident = workflow_identifier or source_execution_id + self._ensure_not_empty(workflow_identifier=ident) data = { - 'description': description + 'description': description, } - if uuidutils.is_uuid_like(workflow_identifier): - data.update({'workflow_id': workflow_identifier}) - else: - data.update({'workflow_name': workflow_identifier}) + if uuidutils.is_uuid_like(source_execution_id): + data.update({'source_execution_id': source_execution_id}) + + if workflow_identifier: + if uuidutils.is_uuid_like(workflow_identifier): + data.update({'workflow_id': workflow_identifier}) + else: + data.update({'workflow_name': workflow_identifier}) if namespace: data.update({'workflow_namespace': namespace}) diff --git a/mistralclient/commands/v2/executions.py b/mistralclient/commands/v2/executions.py index ad50daff..eba5c829 100644 --- a/mistralclient/commands/v2/executions.py +++ b/mistralclient/commands/v2/executions.py @@ -173,6 +173,7 @@ class Create(command.ShowOne): parser.add_argument( 'workflow_identifier', + nargs='?', help='Workflow ID or name. Workflow name will be deprecated since ' 'Mitaka.' ) @@ -199,6 +200,14 @@ class Create(command.ShowOne): default='', help='Execution description' ) + parser.add_argument( + '-s', + dest='source_execution_id', + nargs='?', + help="Workflow Execution id which will allow operators to create " + "a new workflow execution based on the previously successful " + "executed workflow. Example: mistral execution-create -s " + "123e4567-e89b-12d3-a456-426655440000") return parser @@ -220,6 +229,7 @@ class Create(command.ShowOne): parsed_args.namespace, wf_input, parsed_args.description, + parsed_args.source_execution_id, **params ) diff --git a/mistralclient/tests/unit/v2/test_executions.py b/mistralclient/tests/unit/v2/test_executions.py index 361685ef..5415cd8e 100644 --- a/mistralclient/tests/unit/v2/test_executions.py +++ b/mistralclient/tests/unit/v2/test_executions.py @@ -52,6 +52,8 @@ SUB_WF_EXEC = { } } +SOURCE_EXEC = EXEC +SOURCE_EXEC['source_execution_id'] = EXEC['workflow_id'] URL_TEMPLATE = '/executions' URL_TEMPLATE_ID = '/executions/%s' @@ -65,7 +67,7 @@ class TestExecutionsV2(base.BaseClientV2Test): body = { 'workflow_name': EXEC['workflow_name'], 'description': '', - 'input': json.dumps(EXEC['input']), + 'input': json.dumps(EXEC['input']) } ex = self.executions.create( @@ -91,7 +93,7 @@ class TestExecutionsV2(base.BaseClientV2Test): body = { 'workflow_id': EXEC['workflow_id'], 'description': '', - 'input': json.dumps(EXEC['input']), + 'input': json.dumps(EXEC['input']) } ex = self.executions.create( @@ -108,6 +110,29 @@ class TestExecutionsV2(base.BaseClientV2Test): self.assertDictEqual(body, self.requests_mock.last_request.json()) + def test_create_with_source_execution_id(self): + self.requests_mock.post(self.TEST_URL + URL_TEMPLATE, + json=SOURCE_EXEC, + status_code=201) + + body = { + 'description': '', + 'source_execution_id': SOURCE_EXEC['source_execution_id'] + } + + ex = self.executions.create( + source_execution_id=SOURCE_EXEC['source_execution_id'] + ) + + self.assertIsNotNone(ex) + + self.assertDictEqual( + executions.Execution(self.executions, SOURCE_EXEC).to_dict(), + ex.to_dict() + ) + + self.assertDictEqual(body, self.requests_mock.last_request.json()) + def test_create_failure1(self): self.requests_mock.post(self.TEST_URL + URL_TEMPLATE, json=EXEC,