diff --git a/rally-jobs/extra/mistral_input.json b/rally-jobs/extra/mistral_input.json new file mode 100644 index 00000000..2d3edf39 --- /dev/null +++ b/rally-jobs/extra/mistral_input.json @@ -0,0 +1 @@ +{"input1": "value1", "some_json_input": {"a": "b"}} \ No newline at end of file diff --git a/rally-jobs/extra/mistral_params.json b/rally-jobs/extra/mistral_params.json new file mode 100644 index 00000000..e75c3f82 --- /dev/null +++ b/rally-jobs/extra/mistral_params.json @@ -0,0 +1 @@ +{"env": {"env_param": "env_param_value"}} \ No newline at end of file diff --git a/rally-jobs/extra/mistral_wb.yaml b/rally-jobs/extra/mistral_wb.yaml index ab20f7f1..98ccdceb 100644 --- a/rally-jobs/extra/mistral_wb.yaml +++ b/rally-jobs/extra/mistral_wb.yaml @@ -6,6 +6,9 @@ name: wb workflows: wf1: type: direct + input: + - input1: input1 + - some_json_input: {} tasks: hello: action: std.echo output="Hello" diff --git a/rally-jobs/rally-mistral.yaml b/rally-jobs/rally-mistral.yaml index e0c9281d..5ecb0ffc 100644 --- a/rally-jobs/rally-mistral.yaml +++ b/rally-jobs/rally-mistral.yaml @@ -64,6 +64,8 @@ args: definition: "~/.rally/extra/mistral_wb.yaml" workflow_name: "wf1" + params: "~/.rally/extra/mistral_params.json" + wf_input: "~/.rally/extra/mistral_input.json" do_delete: true runner: type: "constant" diff --git a/rally/plugins/openstack/scenarios/mistral/executions.py b/rally/plugins/openstack/scenarios/mistral/executions.py index f4d1dea8..9667e8f8 100644 --- a/rally/plugins/openstack/scenarios/mistral/executions.py +++ b/rally/plugins/openstack/scenarios/mistral/executions.py @@ -12,6 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. +import json + import six import yaml @@ -52,6 +54,8 @@ class ListExecutions(utils.MistralScenario): @validation.required_parameters("definition") @validation.file_exists("definition") @types.convert(definition={"type": "file"}) +@types.convert(params={"type": "file"}) +@types.convert(wf_input={"type": "file"}) @validation.required_clients("mistral") @validation.required_openstack(users=True) @validation.required_services(consts.Service.MISTRAL) @@ -61,7 +65,8 @@ class ListExecutions(utils.MistralScenario): context={"cleanup": ["mistral"]}) class CreateExecutionFromWorkbook(utils.MistralScenario): - def run(self, definition, workflow_name=None, do_delete=False): + def run(self, definition, workflow_name=None, wf_input=None, params=None, + do_delete=False): """Scenario tests execution creation and deletion. This scenario is a very useful tool to measure the @@ -73,7 +78,10 @@ class CreateExecutionFromWorkbook(utils.MistralScenario): one of the to workflows in the definition. If no workflow_name is passed, one of the workflows in the definition will be taken. - + :param wf_input: file containing a json string of mistral workflow + input + :param params: file containing a json string of mistral params + (the string is the place to pass the environment) :param do_delete: if False than it allows to check performance in "create only" mode. """ @@ -85,7 +93,13 @@ class CreateExecutionFromWorkbook(utils.MistralScenario): workflow_name = six.next(six.iterkeys(wb_def["workflows"])) workflow_identifier = ".".join([wb.name, workflow_name]) - ex = self._create_execution(workflow_identifier) + + if not params: + params = {} + else: + params = json.loads(params) + + ex = self._create_execution(workflow_identifier, wf_input, **params) if do_delete: self._delete_workbook(wb.name) diff --git a/rally/plugins/openstack/scenarios/mistral/utils.py b/rally/plugins/openstack/scenarios/mistral/utils.py index b480b60d..9ee2ec4c 100644 --- a/rally/plugins/openstack/scenarios/mistral/utils.py +++ b/rally/plugins/openstack/scenarios/mistral/utils.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. + from oslo_config import cfg import yaml @@ -78,15 +79,18 @@ class MistralScenario(scenario.OpenStackScenario): sort_dirs=sort_dirs) @atomic.action_timer("mistral.create_execution") - def _create_execution(self, workflow_identifier): + def _create_execution(self, workflow_identifier, wf_input=None, **params): """Create a new execution. :param workflow_identifier: name or id of the workflow to execute + :param input_: json string of mistral workflow input + :param params: optional mistral params (this is the place to pass + environment). :returns: executions object """ execution = self.clients("mistral").executions.create( - workflow_identifier) + workflow_identifier, workflow_input=wf_input, **params) execution = utils.wait_for_status( execution, ready_statuses=["SUCCESS"], failure_statuses=["ERROR"], diff --git a/samples/tasks/scenarios/mistral/create-execution-with-inputs.json b/samples/tasks/scenarios/mistral/create-execution-with-inputs.json new file mode 100644 index 00000000..ab341860 --- /dev/null +++ b/samples/tasks/scenarios/mistral/create-execution-with-inputs.json @@ -0,0 +1,26 @@ +{ + "MistralExecutions.create_execution_from_workbook": [ + { + "args": { + "definition": "rally-jobs/extra/mistral_wb.yaml", + "wf_input": "rally-jobs/extra/mistral_input.json" + }, + "runner": { + "type": "constant", + "times": 20, + "concurrency": 5 + }, + "context": { + "users": { + "tenants": 2, + "users_per_tenant": 2 + } + }, + "sla": { + "failure_rate": { + "max": 0 + } + } + } + ] +} diff --git a/samples/tasks/scenarios/mistral/create-execution-with-inputs.yaml b/samples/tasks/scenarios/mistral/create-execution-with-inputs.yaml new file mode 100644 index 00000000..bc97406b --- /dev/null +++ b/samples/tasks/scenarios/mistral/create-execution-with-inputs.yaml @@ -0,0 +1,18 @@ +--- + MistralExecutions.create_execution_from_workbook: + - + args: + definition: rally-jobs/extra/mistral_wb.yaml + wf_input: rally-jobs/extra/mistral_input.json + runner: + type: "constant" + times: 20 + concurrency: 5 + context: + users: + tenants: 2 + users_per_tenant: 2 + sla: + failure_rate: + max: 0 + diff --git a/samples/tasks/scenarios/mistral/create-execution-with-params.json b/samples/tasks/scenarios/mistral/create-execution-with-params.json new file mode 100644 index 00000000..07c59a9d --- /dev/null +++ b/samples/tasks/scenarios/mistral/create-execution-with-params.json @@ -0,0 +1,26 @@ +{ + "MistralExecutions.create_execution_from_workbook": [ + { + "args": { + "definition": "rally-jobs/extra/mistral_wb.yaml", + "params": "rally-jobs/extra/mistral_params.json" + }, + "runner": { + "type": "constant", + "times": 20, + "concurrency": 5 + }, + "context": { + "users": { + "tenants": 2, + "users_per_tenant": 2 + } + }, + "sla": { + "failure_rate": { + "max": 0 + } + } + } + ] +} diff --git a/samples/tasks/scenarios/mistral/create-execution-with-params.yaml b/samples/tasks/scenarios/mistral/create-execution-with-params.yaml new file mode 100644 index 00000000..26bb3d1e --- /dev/null +++ b/samples/tasks/scenarios/mistral/create-execution-with-params.yaml @@ -0,0 +1,18 @@ +--- + MistralExecutions.create_execution_from_workbook: + - + args: + definition: rally-jobs/extra/mistral_wb.yaml + params: rally-jobs/extra/mistral_params.json + runner: + type: "constant" + times: 20 + concurrency: 5 + context: + users: + tenants: 2 + users_per_tenant: 2 + sla: + failure_rate: + max: 0 + diff --git a/tests/unit/plugins/openstack/scenarios/mistral/test_executions.py b/tests/unit/plugins/openstack/scenarios/mistral/test_executions.py index aaef7135..dbd49fa6 100644 --- a/tests/unit/plugins/openstack/scenarios/mistral/test_executions.py +++ b/tests/unit/plugins/openstack/scenarios/mistral/test_executions.py @@ -59,6 +59,9 @@ workflows: action: std.noop """ +PARAMS_EXAMPLE = {"env": {"env_param": "env_param_value"}} +INPUT_EXAMPLE = """{"input1": "value1", "some_json_input": {"a": "b"}}""" + WB = type("obj", (object,), {"name": "wb", "definition": WB_DEFINITION})() WB_ONE_WF = ( type("obj", (object,), {"name": "wb", "definition": WB_DEF_ONE_WF})() @@ -83,6 +86,33 @@ class MistralExecutionsTestCase(test.ScenarioTestCase): self.assertEqual(1, mock__create_workbook.called) self.assertEqual(1, mock__create_execution.called) + @mock.patch("%s.CreateExecutionFromWorkbook._create_execution" % BASE) + @mock.patch("%s.CreateExecutionFromWorkbook._create_workbook" % BASE, + return_value=WB) + def test_create_execution_with_input(self, mock__create_workbook, + mock__create_execution): + + executions.CreateExecutionFromWorkbook(self.context).run( + WB_DEFINITION, wf_input=INPUT_EXAMPLE) + + self.assertEqual(1, mock__create_workbook.called) + self.assertEqual(1, mock__create_execution.called) + + @mock.patch("%s.CreateExecutionFromWorkbook._create_execution" % BASE) + @mock.patch("%s.CreateExecutionFromWorkbook._create_workbook" % BASE, + return_value=WB) + @mock.patch("json.loads", return_value=PARAMS_EXAMPLE) + def test_create_execution_with_params(self, mock_loads, + mock__create_workbook, + mock__create_execution): + + executions.CreateExecutionFromWorkbook(self.context).run( + WB_DEFINITION, params=str(PARAMS_EXAMPLE)) + + self.assertEqual(1, mock_loads.called) + self.assertEqual(1, mock__create_workbook.called) + self.assertEqual(1, mock__create_execution.called) + @mock.patch("%s.CreateExecutionFromWorkbook._create_execution" % BASE) @mock.patch("%s.CreateExecutionFromWorkbook._create_workbook" % BASE, return_value=WB) @@ -98,7 +128,7 @@ class MistralExecutionsTestCase(test.ScenarioTestCase): # we concatenate workbook name with the workflow name in the test # the workbook name is not random because we mock the method that # adds the random part - mock__create_execution.assert_called_once_with("wb.wf4") + mock__create_execution.assert_called_once_with("wb.wf4", None,) @mock.patch("%s.CreateExecutionFromWorkbook._delete_execution" % BASE) @mock.patch("%s.CreateExecutionFromWorkbook._delete_workbook" % BASE) @@ -137,7 +167,7 @@ class MistralExecutionsTestCase(test.ScenarioTestCase): # we concatenate workbook name with the workflow name in the test # the workbook name is not random because we mock the method that # adds the random part - mock__create_execution.assert_called_once_with("wb.wf4") + mock__create_execution.assert_called_once_with("wb.wf4", None) @mock.patch("%s.CreateExecutionFromWorkbook._delete_execution" % BASE) @mock.patch("%s.CreateExecutionFromWorkbook._delete_workbook" % BASE) @@ -159,4 +189,4 @@ class MistralExecutionsTestCase(test.ScenarioTestCase): # we concatenate workbook name with the workflow name in the test # the workbook name is not random because we mock the method that # adds the random part - mock__create_execution.assert_called_once_with("wb.wf1") + mock__create_execution.assert_called_once_with("wb.wf1", None) diff --git a/tests/unit/plugins/openstack/scenarios/mistral/test_utils.py b/tests/unit/plugins/openstack/scenarios/mistral/test_utils.py index 7801cac4..c67632ee 100644 --- a/tests/unit/plugins/openstack/scenarios/mistral/test_utils.py +++ b/tests/unit/plugins/openstack/scenarios/mistral/test_utils.py @@ -13,11 +13,14 @@ # License for the specific language governing permissions and limitations # under the License. + from rally.plugins.openstack.scenarios.mistral import utils from tests.unit import fakes from tests.unit import test MISTRAL_UTILS = "rally.plugins.openstack.scenarios.mistral.utils" +PARAMS_EXAMPLE = {"env": {"env_param": "param_value"}} +INPUT_EXAMPLE = """{"input1": "value1", "some_json_input": {"a": "b"}}""" class MistralScenarioTestCase(test.ScenarioTestCase): @@ -80,7 +83,56 @@ class MistralScenarioTestCase(test.ScenarioTestCase): scenario._create_execution("%s" % wf_name) ) - mock_create_exec.assert_called_once_with(wf_name) + mock_create_exec.assert_called_once_with(wf_name, workflow_input=None) + + args, kwargs = mock_wait_for_status.call_args + self.assertEqual(mock_create_exec.return_value, args[0]) + self.assertEqual(["ERROR"], kwargs["failure_statuses"]) + self.assertEqual(["SUCCESS"], kwargs["ready_statuses"]) + self._test_atomic_action_timer( + scenario.atomic_actions(), + "mistral.create_execution" + ) + + def test_create_execution_with_input(self): + scenario = utils.MistralScenario(context=self.context) + + mock_wait_for_status = self.mock_wait_for_status.mock + wf_name = "fake_wf_name" + mock_create_exec = self.clients("mistral").executions.create + + self.assertEqual( + mock_wait_for_status.return_value, + scenario._create_execution( + wf_name, wf_input=str(INPUT_EXAMPLE)) + ) + + mock_create_exec.assert_called_once_with(wf_name, + workflow_input=INPUT_EXAMPLE) + + def test_create_execution_with_params(self): + scenario = utils.MistralScenario(context=self.context) + + mock_wait_for_status = self.mock_wait_for_status.mock + wf_name = "fake_wf_name" + mock_create_exec = self.clients("mistral").executions.create + + self.assertEqual( + mock_wait_for_status.return_value, + scenario._create_execution( + wf_name, **PARAMS_EXAMPLE) + ) + mock_create_exec.assert_called_once_with(wf_name, workflow_input=None, + **PARAMS_EXAMPLE) + + args, kwargs = mock_wait_for_status.call_args + self.assertEqual(mock_create_exec.return_value, args[0]) + self.assertEqual(["ERROR"], kwargs["failure_statuses"]) + self.assertEqual(["SUCCESS"], kwargs["ready_statuses"]) + self._test_atomic_action_timer( + scenario.atomic_actions(), + "mistral.create_execution" + ) args, kwargs = mock_wait_for_status.call_args self.assertEqual(mock_create_exec.return_value, args[0])