# Copyright 2013: Mirantis Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime as dt import json import os.path import sys import ddt import mock import six import rally from rally import api from rally.cli import cliutils from rally.cli.commands import task from rally.common import yamlutils as yaml from rally import consts from rally import exceptions from tests.unit import fakes from tests.unit import test @ddt.ddt class TaskCommandsTestCase(test.TestCase): def setUp(self): super(TaskCommandsTestCase, self).setUp() self.task = task.TaskCommands() self.fake_api = fakes.FakeAPI() with mock.patch("rally.api.API.check_db_revision"): self.real_api = api.API() @mock.patch("rally.cli.commands.task.open", create=True) def test__load_and_validate_task(self, mock_open): input_task = "{'ab': {{test}}}" input_args = "{'test': 2}" mock_open.side_effect = [ mock.mock_open(read_data=input_task).return_value, mock.mock_open(read_data="{'test': 1}").return_value ] task_conf = self.task._load_and_validate_task( self.real_api, "in_task", args_file="in_args_path") self.assertEqual({"ab": 1}, task_conf) mock_open.side_effect = [ mock.mock_open(read_data=input_task).return_value ] task_conf = self.task._load_and_validate_task( self.real_api, "in_task", raw_args=input_args) self.assertEqual({"ab": 2}, task_conf) mock_open.side_effect = [ mock.mock_open(read_data=input_task).return_value, mock.mock_open(read_data="{'test': 1}").return_value ] task_conf = self.task._load_and_validate_task( self.real_api, "in_task", raw_args=input_args, args_file="any_file") self.assertEqual({"ab": 2}, task_conf) mock_open.side_effect = [ mock.mock_open(read_data=input_task).return_value, mock.mock_open(read_data="{'test': 1}").return_value ] task_conf = self.task._load_and_validate_task( self.real_api, "in_task", raw_args="test=2", args_file="any_file") self.assertEqual({"ab": 2}, task_conf) @mock.patch("rally.cli.commands.task.open", create=True) def test__load_task_wrong_task_args_file(self, mock_open): def open_return_value(filename): if filename == "in_task": return mock.Mock() else: raise IOError() mock_open.side_effect = open_return_value e = self.assertRaises(task.FailedToLoadTask, self.task._load_and_validate_task, self.fake_api, task_file="in_task", args_file="in_args_path") self.assertEqual("Invalid --task-args-file passed:\n\n\t Error " "reading in_args_path: ", e.format_message()) @mock.patch("rally.cli.commands.task.yaml.safe_load") def test__load_task_wrong_input_task_args(self, mock_safe_load): mock_safe_load.side_effect = yaml.ParserError("foo") # use real file to avoid mocking open task_file = __file__ e = self.assertRaises(task.FailedToLoadTask, self.task._load_and_validate_task, self.real_api, task_file, raw_args="{'test': {}") self.assertEqual("Invalid --task-args passed:\n\n\t Value has to be " "YAML or JSON. Details:\n\nfoo", e.format_message()) mock_safe_load.assert_called_once_with("{'test': {}") # the case #2 mock_safe_load.reset_mock() e = self.assertRaises(task.FailedToLoadTask, self.task._load_and_validate_task, self.real_api, task_file, raw_args="[]") self.assertEqual("Invalid --task-args passed:\n\n\t Value has to be " "YAML or JSON. Details:\n\nfoo", e.format_message()) mock_safe_load.assert_called_once_with("[]") # the case #3 mock_safe_load.reset_mock() e = self.assertRaises(task.FailedToLoadTask, self.task._load_and_validate_task, self.real_api, task_file, raw_args="foo") self.assertEqual("Invalid --task-args passed:\n\n\t Value has to be " "YAML or JSON. Details:\n\nfoo", e.format_message()) mock_safe_load.assert_called_once_with("foo") @mock.patch("rally.cli.commands.task.open", create=True) def test__load_task_task_render_raise_exc(self, mock_open): mock_open.side_effect = [ mock.mock_open(read_data="{'test': {{t}}}").return_value ] e = self.assertRaises(task.FailedToLoadTask, self.task._load_and_validate_task, self.real_api, "in_task") self.assertEqual("Invalid --task passed:\n\n\t Failed to render task " "template.\n\nPlease specify template task argument: " "t", e.format_message()) @mock.patch("rally.cli.commands.task.yaml") @mock.patch("rally.cli.commands.task.open", create=True) def test__load_task_task_not_in_yaml(self, mock_open, mock_yaml): mock_open.side_effect = [ mock.mock_open(read_data="{'test': {}").return_value ] mock_yaml.safe_load.side_effect = Exception("ERROR!!!PANIC!!!") e = self.assertRaises(task.FailedToLoadTask, self.task._load_and_validate_task, self.fake_api, "in_task") self.assertEqual("Invalid --task passed:\n\n\t Wrong format of " "rendered input task. It should be YAML or JSON. " "Details:\n\nERROR!!!PANIC!!!", e.format_message()) def test_load_task_including_other_template(self): other_template_path = os.path.join( os.path.dirname(rally.__file__), os.pardir, "samples/tasks/scenarios/dummy/dummy.json") input_task = "{%% include \"%s\" %%}" % os.path.basename( other_template_path) expect = self.task._load_and_validate_task(self.real_api, other_template_path) with mock.patch("rally.cli.commands.task.open", create=True) as mock_open: mock_open.side_effect = [ mock.mock_open(read_data=input_task).return_value ] input_task_file = os.path.join( os.path.dirname(other_template_path), "input_task.json") actual = self.task._load_and_validate_task(self.real_api, input_task_file) self.assertEqual(expect, actual) @mock.patch("rally.cli.commands.task.open", create=True) def test__load_and_validate_file_failed(self, mock_open): mock_open.side_effect = IOError e = self.assertRaises(task.FailedToLoadTask, self.task._load_and_validate_task, api=self.fake_api, task_file="some_task", raw_args="task_args", args_file="task_args_file") self.assertEqual( "Invalid --task passed:\n\n\t Error reading some_task: ", e.format_message()) @mock.patch("rally.cli.commands.task.version") @mock.patch("rally.cli.commands.task.TaskCommands.use") @mock.patch("rally.cli.commands.task.TaskCommands._detailed") @mock.patch("rally.cli.commands.task.TaskCommands._load_and_validate_task", return_value={"some": "json"}) def test_start(self, mock__load_and_validate_task, mock__detailed, mock_use, mock_version): deployment_id = "e0617de9-77d1-4875-9b49-9d5789e29f20" task_path = "path_to_config.json" fake_task = fakes.FakeTask(uuid="some_new_uuid", tags=["tag"]) mock__detailed.return_value = 1 self.fake_api.task.create.return_value = fake_task self.fake_api.task.validate.return_value = fakes.FakeTask( some="json", uuid="some_uuid", temporary=True) val = self.task.start(self.fake_api, task_path, deployment_id, do_use=True) self.assertEqual(2, val) mock_version.version_string.assert_called_once_with() self.fake_api.task.create.assert_called_once_with( deployment=deployment_id, tags=None) self.fake_api.task.start.assert_called_once_with( deployment=deployment_id, config=mock__load_and_validate_task.return_value, task=fake_task["uuid"], abort_on_sla_failure=False) mock__load_and_validate_task.assert_called_once_with( self.fake_api, task_path, args_file=None, raw_args=None) mock_use.assert_called_once_with(self.fake_api, "some_new_uuid") mock__detailed.assert_called_once_with(self.fake_api, task_id=fake_task["uuid"]) mock__detailed.return_value = 0 val1 = self.task.start(self.fake_api, task_path, deployment_id, do_use=True) self.assertEqual(0, val1) @mock.patch("rally.cli.commands.task.TaskCommands._detailed") @mock.patch("rally.cli.commands.task.TaskCommands._load_and_validate_task", return_value="some_config") def test_start_on_unfinished_deployment(self, mock__load_and_validate_task, mock__detailed): deployment_id = "e0617de9-77d1-4875-9b49-9d5789e29f20" deployment_name = "xxx_name" task_path = "path_to_config.json" fake_task = fakes.FakeTask(uuid="some_new_uuid", tag="tag") self.fake_api.task.create.return_value = fake_task exc = exceptions.DeploymentNotFinishedStatus( name=deployment_name, uuid=deployment_id, status=consts.DeployStatus.DEPLOY_INIT) self.fake_api.task.create.side_effect = exc self.assertEqual(1, self.task.start(self.fake_api, task_path, deployment="any", tags=["some_tag"])) self.assertFalse(mock__detailed.called) @mock.patch("rally.cli.commands.task.TaskCommands._detailed") @mock.patch("rally.cli.commands.task.TaskCommands._load_and_validate_task", return_value="some_config") def test_start_with_task_args(self, mock__load_and_validate_task, mock__detailed): fake_task = fakes.FakeTask(uuid="new_uuid", tags=["some_tag"]) self.fake_api.task.create.return_value = fakes.FakeTask( uuid="new_uuid", tags=["some_tag"]) self.fake_api.task.validate.return_value = fakes.FakeTask( uuid="some_id") task_path = "path_to_config.json" task_args = "task_args" task_args_file = "task_args_file" self.task.start(self.fake_api, task_path, deployment="any", task_args=task_args, task_args_file=task_args_file, tags=["some_tag"]) mock__load_and_validate_task.assert_called_once_with( self.fake_api, task_path, raw_args=task_args, args_file=task_args_file) self.fake_api.task.start.assert_called_once_with( deployment="any", config=mock__load_and_validate_task.return_value, task=fake_task["uuid"], abort_on_sla_failure=False) mock__detailed.assert_called_once_with( self.fake_api, task_id=fake_task["uuid"]) self.fake_api.task.create.assert_called_once_with( deployment="any", tags=["some_tag"]) @mock.patch("rally.cli.commands.task.envutils.get_global") def test_start_no_deployment_id(self, mock_get_global): mock_get_global.side_effect = exceptions.InvalidArgumentsException self.assertRaises(exceptions.InvalidArgumentsException, self.task.start, "path_to_config.json", None) @mock.patch("rally.cli.commands.task.TaskCommands._detailed") @mock.patch("rally.cli.commands.task.TaskCommands._load_and_validate_task") def test_start_invalid_task(self, mock__load_and_validate_task, mock__detailed): task_obj = fakes.FakeTask(temporary=False, tag="tag", uuid="uuid") self.fake_api.task.create.return_value = task_obj exc = exceptions.InvalidTaskException("foo") mock__load_and_validate_task.side_effect = exc self.assertRaises(exceptions.InvalidTaskException, self.task.start, self.fake_api, "task_path", "deployment", tags=["tag"]) self.assertFalse(self.fake_api.task.create.called) self.assertFalse(self.fake_api.task.start.called) # the case 2 task_cfg = {"some": "json"} mock__load_and_validate_task.side_effect = (task_cfg, ) self.fake_api.task.start.side_effect = KeyError() self.assertRaises(KeyError, self.task.start, self.fake_api, "task_path", "deployment", tags=["tag"]) self.fake_api.task.create.assert_called_once_with( deployment="deployment", tags=["tag"]) self.fake_api.task.start.assert_called_once_with( deployment="deployment", config=task_cfg, task=task_obj["uuid"], abort_on_sla_failure=False) self.assertFalse(mock__detailed.called) def test_abort(self): test_uuid = "17860c43-2274-498d-8669-448eff7b073f" self.task.abort(self.fake_api, test_uuid) self.fake_api.task.abort.assert_called_once_with( task_uuid=test_uuid, soft=False, wait=True) @mock.patch("rally.cli.commands.task.envutils.get_global") def test_abort_no_task_id(self, mock_get_global): mock_get_global.side_effect = exceptions.InvalidArgumentsException self.assertRaises(exceptions.InvalidArgumentsException, self.task.abort, self.fake_api, None) def test_status(self): test_uuid = "a3e7cefb-bec2-4802-89f6-410cc31f71af" value = {"task_id": "task", "status": "status"} self.fake_api.task.get.return_value = value self.task.status(self.fake_api, test_uuid) self.fake_api.task.get.assert_called_once_with(task_id=test_uuid) @mock.patch("rally.cli.commands.task.envutils.get_global") def test_status_no_task_id(self, mock_get_global): mock_get_global.side_effect = exceptions.InvalidArgumentsException self.assertRaises(exceptions.InvalidArgumentsException, self.task.status, None) @ddt.data({"iterations_data": False, "has_output": True, "filters": None}, {"iterations_data": True, "has_output": False, "filters": ["scenario=fake_name", "sla_failures"]}) @ddt.unpack def test_detailed(self, iterations_data, has_output, filters): test_uuid = "c0d874d4-7195-4fd5-8688-abe82bfad36f" detailed_value = { "id": "task", "uuid": test_uuid, "pass_sla": False, "status": "finished", "subtasks": [{"workloads": [{ "name": "fake_name", "position": "fake_pos", "args": "args", "contexts": "context", "sla": "sla", "runner": "runner", "hooks": [], "statistics": { "durations": { "atomics": [ { "name": "foo", "display_name": "foo (x2)", "count_per_iteration": 2, "children": [ {"name": "inner_foo", "display_name": "inner_foo", "count_per_iteration": 1, "children": [], "data": { "min": 1, "median": 2, "90%ile": 1.5, "95%ile": 1.6, "max": 3, "avg": 1.4, "success": 3, "iteration_count": 3} } ], "data": { "min": 1, "median": 2, "90%ile": 1.5, "95%ile": 1.6, "max": 3, "avg": 1.4, "success": 3, "iteration_count": 3}}, { "name": "bar", "display_name": "bar", "count_per_iteration": 1, "children": [], "data": { "min": 1.1, "median": 2.2, "90%ile": 1.6, "95%ile": 1.65, "max": 3, "avg": 1.5, "success": 3, "iteration_count": 3} }], "total": { "name": "total", "display_name": "bar", "count_per_iteration": 1, "children": [], "data": { "min": 1, "median": 2.1, "90%ile": 1.55, "95%ile": 1.62, "max": 3, "avg": 1.45, "success": 6, "iteration_count": 6}}}}, "load_duration": 3.2, "full_duration": 3.5, "total_iteration_count": 4, "data": [ { "duration": 0.9, "idle_duration": 0.1, "output": {"additive": [], "complete": []}, "atomic_actions": [ {"name": "foo", "started_at": 0.0, "finished_at": 0.6, "children": []}, {"name": "bar", "started_at": 0.6, "finished_at": 1.3, "children": []} ], "error": ["type", "message", "traceback"] }, { "duration": 1.2, "idle_duration": 0.3, "output": {"additive": [], "complete": []}, "atomic_actions": [ {"name": "foo", "started_at": 0.0, "finished_at": 0.6, "children": []}, {"name": "bar", "started_at": 0.6, "finished_at": 1.3, "children": []} ], "error": ["type", "message", "traceback"] }, { "duration": 0.7, "idle_duration": 0.5, "output": { "additive": [ {"data": [("foo", 0.6), ("bar", 0.7)], "title": "Scenario output", "description": "", "chart_plugin": "StackedArea"} ], "complete": [] }, "atomic_actions": [ {"name": "foo", "started_at": 0.0, "finished_at": 0.6, "children": []}, {"name": "bar", "started_at": 0.6, "finished_at": 1.3, "children": []} ], "error": ["type", "message", "traceback"] }, { "duration": 0.5, "idle_duration": 0.5, "atomic_actions": [ {"name": "foo", "started_at": 0.0, "finished_at": 0.6, "children": []}, {"name": "bar", "started_at": 0.6, "finished_at": 1.3, "children": []} ], "error": ["type", "message", "traceback"] }], }]}]} if has_output: detailed_value["subtasks"][0]["workloads"][0]["output"] = { "additive": [], "complete": []} self.fake_api.task.get.return_value = detailed_value self.task.detailed(self.fake_api, test_uuid, iterations_data=iterations_data, filters=filters) self.fake_api.task.get.assert_called_once_with( task_id=test_uuid, detailed=True) @mock.patch("rally.cli.commands.task.sys.stdout") @mock.patch("rally.cli.commands.task.logging") @ddt.data({"debug": True}, {"debug": False}) @ddt.unpack def test_detailed_task_failed(self, mock_logging, mock_stdout, debug): test_uuid = "test_task_id" value = { "id": "task", "uuid": test_uuid, "status": consts.TaskStatus.CRASHED, "results": [], "validation_result": {"etype": "error_type", "msg": "error_message", "trace": "error_traceback"} } self.fake_api.task.get.return_value = value mock_logging.is_debug.return_value = debug self.task.detailed(self.fake_api, test_uuid) if debug: expected_calls = [ mock.call("Task test_task_id: crashed"), mock.call("%s" % value["validation_result"]["trace"])] mock_stdout.write.assert_has_calls(expected_calls, any_order=True) else: expected_calls = [ mock.call("Task test_task_id: crashed"), mock.call("%s" % value["validation_result"]["etype"]), mock.call("%s" % value["validation_result"]["msg"]), mock.call("\nFor more details run:\n" "rally -d task detailed %s" % test_uuid)] mock_stdout.write.assert_has_calls(expected_calls, any_order=True) @mock.patch("rally.cli.commands.task.sys.stdout") def test_detailed_task_status_not_in_finished_abort(self, mock_stdout): test_uuid = "test_task_id" value = { "id": "task", "uuid": test_uuid, "status": consts.TaskStatus.INIT, "results": [] } self.fake_api.task.get.return_value = value self.task.detailed(self.fake_api, test_uuid) expected_calls = [mock.call("Task test_task_id: init"), mock.call("\nThe task test_task_id marked as " "'init'. Results available when it " "is 'finished'.")] mock_stdout.write.assert_has_calls(expected_calls, any_order=True) @mock.patch("rally.cli.commands.task.envutils.get_global") def test_detailed_no_task_id(self, mock_get_global): mock_get_global.side_effect = exceptions.InvalidArgumentsException self.assertRaises(exceptions.InvalidArgumentsException, self.task.detailed, None) def test_detailed_wrong_id(self): test_uuid = "eb290c30-38d8-4c8f-bbcc-fc8f74b004ae" self.fake_api.task.get.side_effect = None self.task.detailed(self.fake_api, test_uuid) self.fake_api.task.get.assert_called_once_with( task_id=test_uuid, detailed=True) def _make_task(self, status=None, data=None): return { "status": status or consts.TaskStatus.FINISHED, "subtasks": [{"workloads": [{ "full_duration": 1, "load_duration": 2, "created_at": "2017-09-27T07:22:55", "name": "Foo.bar", "description": "descr", "position": 2, "args": {"key1": "value1"}, "runner_type": "rruunneerr", "runner": {"arg1": "args2"}, "hooks": [], "sla": {"failure_rate": {"max": 0}}, "sla_results": {"sla": [{"success": True}]}, "contexts": {"users": {}}, "data": data or []}]}]} @mock.patch("rally.cli.commands.task.json.dumps") def test_results(self, mock_json_dumps): task_id = "foo_task_id" task_obj = self._make_task(data=[{"atomic_actions": {"foo": 1.1}}]) task_obj["subtasks"][0]["workloads"][0]["hooks"] = [{ "config": { "action": ("foo", "arg"), "trigger": ("bar", "arg2") }, "summary": {"success": 1}} ] def fix_r(workload): cfg = workload["runner"] cfg["type"] = workload["runner_type"] return cfg result = map( lambda x: { "key": {"kw": {"sla": x["sla"], "args": x["args"], "context": x["contexts"], "runner": fix_r(x), "hooks": [{"description": "", "name": "foo", "args": "arg", "trigger": {"name": "bar", "args": "arg2"}}]}, "pos": x["position"], "name": x["name"], "description": x["description"]}, "result": x["data"], "load_duration": x["load_duration"], "full_duration": x["full_duration"], "created_at": dt.datetime.strptime( x["created_at"], "%Y-%m-%dT%H:%M:%S").strftime( "%Y-%d-%mT%H:%M:%S"), "hooks": [{ "config": {"description": "", "name": "foo", "args": "arg", "trigger": {"name": "bar", "args": "arg2"}}, "summary": {"success": 1}}], "sla": x["sla_results"]["sla"]}, task_obj["subtasks"][0]["workloads"]) self.fake_api.task.get.return_value = task_obj self.assertIsNone(self.task.results(self.fake_api, task_id)) self.assertEqual(1, mock_json_dumps.call_count) self.assertEqual(1, len(mock_json_dumps.call_args[0])) self.assertSequenceEqual(result, mock_json_dumps.call_args[0][0]) self.assertEqual({"sort_keys": False, "indent": 4}, mock_json_dumps.call_args[1]) self.fake_api.task.get.assert_called_once_with( task_id=task_id, detailed=True) @mock.patch("rally.cli.commands.task.sys.stdout") def test_results_no_data(self, mock_stdout): task_id = "foo" self.fake_api.task.get.return_value = self._make_task( status=consts.TaskStatus.CRASHED) self.assertEqual(1, self.task.results(self.fake_api, task_id)) self.fake_api.task.get.assert_called_once_with( task_id=task_id, detailed=True) expected_out = ("Task status is %s. Results " "available when it is one of %s.") % ( consts.TaskStatus.CRASHED, ", ".join((consts.TaskStatus.FINISHED, consts.TaskStatus.ABORTED))) mock_stdout.write.assert_has_calls([mock.call(expected_out)]) def test_trends(self): self.task.export = mock.MagicMock() self.task.trends(self.fake_api, tasks=["uuid"], out="output.html") self.task.export.assert_called_once_with( self.fake_api, tasks=["uuid"], output_type="trends-html", output_dest="output.html", open_it=False) def test_trends_no_tasks_given(self): ret = self.task.trends(self.fake_api, tasks=[], out="output.html", out_format="html") self.assertEqual(1, ret) def test_report(self): self.task.export = mock.MagicMock() self.task.report(self.fake_api, tasks="uuid", out="out", open_it=False, out_format="junit-xml") self.task.export.assert_called_once_with( self.fake_api, tasks="uuid", output_type="junit-xml", output_dest="out", open_it=False, deployment=None ) @mock.patch("rally.cli.commands.task.cliutils.print_list") @mock.patch("rally.cli.commands.task.envutils.get_global", return_value="123456789") def test_list(self, mock_get_global, mock_print_list): self.fake_api.task.list.return_value = [ {"uuid": "a", "created_at": "2007-01-01T00:00:01", "updated_at": "2007-01-01T00:00:03", "status": consts.TaskStatus.RUNNING, "tags": ["d"], "deployment_name": "some_name"}] self.task.list(self.fake_api, status="running") self.fake_api.task.list.assert_called_once_with( deployment=mock_get_global.return_value, status=consts.TaskStatus.RUNNING) headers = ["UUID", "Deployment name", "Created at", "Load duration", "Status", "Tag(s)"] mock_print_list.assert_called_once_with( self.fake_api.task.list.return_value, fields=headers, normalize_field_names=True, sortby_index=headers.index("Created at"), formatters=mock.ANY) @mock.patch("rally.cli.commands.task.envutils.get_global", return_value="123456789") def test_list_uuids_only(self, mock_get_global): self.fake_api.task.list.return_value = [ {"uuid": "a", "created_at": "2007-01-01T00:00:01", "updated_at": "2007-01-01T00:00:03", "status": consts.TaskStatus.RUNNING, "tags": ["d"], "deployment_name": "some_name"}] out = six.StringIO() with mock.patch.object(sys, "stdout", new=out): self.task.list(self.fake_api, status="running", uuids_only=True) self.assertEqual("a\n", out.getvalue()) self.fake_api.task.list.assert_called_once_with( deployment=mock_get_global.return_value, status=consts.TaskStatus.RUNNING) def test_list_wrong_status(self): self.assertEqual(1, self.task.list(self.fake_api, deployment="fake", status="wrong non existing status")) def test_list_no_results(self): self.fake_api.task.list.return_value = [] self.assertIsNone(self.task.list(self.fake_api, deployment="fake", all_deployments=True)) self.fake_api.task.list.assert_called_once_with() self.fake_api.task.list.reset_mock() self.assertIsNone(self.task.list(self.fake_api, deployment="d", status=consts.TaskStatus.RUNNING)) self.fake_api.task.list.assert_called_once_with( deployment="d", status=consts.TaskStatus.RUNNING) @mock.patch("rally.cli.commands.task.envutils.get_global", return_value="123456789") def test_list_output(self, mock_get_global): self.fake_api.task.list.return_value = [ {"uuid": "UUID-1", "created_at": "2007-01-01T00:00:01", "task_duration": 0.0000009, "status": consts.TaskStatus.INIT, "tags": [], "deployment_name": "some_name"}, {"uuid": "UUID-2", "created_at": "2007-02-01T00:00:01", "task_duration": 123.99992, "status": consts.TaskStatus.FINISHED, "tags": ["tag-1", "tag-2"], "deployment_name": "some_name"}] # It is a hard task to mock default value of function argument, so we # need to apply this workaround original_print_list = cliutils.print_list print_list_calls = [] def print_list(*args, **kwargs): print_list_calls.append(six.StringIO()) kwargs["out"] = print_list_calls[-1] original_print_list(*args, **kwargs) with mock.patch.object(task.cliutils, "print_list", new=print_list): self.task.list(self.fake_api, status="running") self.assertEqual(1, len(print_list_calls)) self.assertEqual( "+--------+-----------------+---------------------" "+---------------+----------+------------------+\n" "| UUID | Deployment name | Created at " "| Load duration | Status | Tag(s) |\n" "+--------+-----------------+---------------------" "+---------------+----------+------------------+\n" "| UUID-1 | some_name | 2007-01-01 00:00:01 " "| 0.0 | init | |\n" "| UUID-2 | some_name | 2007-02-01 00:00:01 " "| 124.0 | finished | 'tag-1', 'tag-2' |\n" "+--------+-----------------+---------------------" "+---------------+----------+------------------+\n", print_list_calls[0].getvalue()) def test_delete(self): task_uuid = "8dcb9c5e-d60b-4022-8975-b5987c7833f7" force = False self.task.delete(self.fake_api, task_uuid, force=force) self.fake_api.task.delete.assert_called_once_with( task_uuid=task_uuid, force=force) def test_delete_multiple_uuid(self): task_uuids = ["4bf35b06-5916-484f-9547-12dce94902b7", "52cad69d-d3e4-47e1-b445-dec9c5858fe8", "6a3cb11c-ac75-41e7-8ae7-935732bfb48f", "018af931-0e5a-40d5-9d6f-b13f4a3a09fc"] force = False self.task.delete(self.fake_api, task_uuids, force=force) self.assertTrue( self.fake_api.task.delete.call_count == len(task_uuids)) expected_calls = [mock.call(task_uuid=task_uuid, force=force) for task_uuid in task_uuids] self.assertTrue(self.fake_api.task.delete.mock_calls == expected_calls) @mock.patch("rally.cli.commands.task.cliutils.print_list") def test_sla_check(self, mock_print_list): task_obj = self._make_task() task_obj["subtasks"][0]["workloads"][0]["sla_results"]["sla"] = [ {"benchmark": "KeystoneBasic.create_user", "criterion": "max_seconds_per_iteration", "pos": 0, "success": False, "detail": "Max foo, actually bar"}] self.fake_api.task.get.return_value = task_obj result = self.task.sla_check(self.fake_api, task_id="fake_task_id") self.assertEqual(1, result) self.fake_api.task.get.assert_called_with( task_id="fake_task_id", detailed=True) task_obj["subtasks"][0]["workloads"][0]["sla_results"]["sla"][0][ "success"] = True result = self.task.sla_check(self.fake_api, task_id="fake_task_id", tojson=True) self.assertEqual(0, result) @mock.patch("rally.cli.commands.task.os.path.isfile", return_value=True) @mock.patch("rally.cli.commands.task.open", side_effect=mock.mock_open(read_data="{\"some\": \"json\"}"), create=True) def test_validate(self, mock_open, mock_os_path_isfile): self.fake_api.task.render_template = self.real_api.task.render_template self.task.validate(self.fake_api, "path_to_config.json", "fake_id") self.fake_api.task.validate.assert_called_once_with( deployment="fake_id", config={"some": "json"}) @mock.patch("rally.cli.commands.task.TaskCommands._load_and_validate_task", side_effect=task.FailedToLoadTask) def test_validate_failed_to_load_task(self, mock__load_and_validate_task): args = "args" args_file = "args_file" mock__load_and_validate_task.side_effect = KeyError("foo") self.assertRaises(KeyError, self.task.validate, self.real_api, "path_to_task", "fake_deployment_id", task_args=args, task_args_file=args_file) self.assertFalse(self.fake_api.task.validate.called) mock__load_and_validate_task.assert_called_once_with( self.real_api, "path_to_task", raw_args=args, args_file=args_file) @mock.patch("rally.cli.commands.task.TaskCommands._load_and_validate_task") def test_validate_invalid(self, mock__load_and_validate_task): exc = exceptions.InvalidTaskException("foo") self.fake_api.task.validate.side_effect = exc self.assertRaises(exceptions.InvalidTaskException, self.task.validate, self.fake_api, "path_to_task", "deployment") self.fake_api.task.validate.assert_called_once_with( deployment="deployment", config=mock__load_and_validate_task.return_value) @mock.patch("rally.common.fileutils._rewrite_env_file") def test_use(self, mock__rewrite_env_file): task_id = "80422553-5774-44bd-98ac-38bd8c7a0feb" self.task.use(self.fake_api, task_id) mock__rewrite_env_file.assert_called_once_with( os.path.expanduser("~/.rally/globals"), ["RALLY_TASK=%s\n" % task_id]) def test_use_not_found(self): task_id = "ddc3f8ba-082a-496d-b18f-72cdf5c10a14" exc = exceptions.DBRecordNotFound(criteria="uuid: %s" % task_id, table="tasks") self.fake_api.task.get.side_effect = exc self.assertRaises(exceptions.DBRecordNotFound, self.task.use, self.fake_api, task_id) @mock.patch("rally.cli.commands.task.os.path") @mock.patch("rally.cli.commands.task.webbrowser.open_new_tab") @mock.patch("rally.cli.commands.task.open", create=True) @mock.patch("rally.cli.commands.task.print") def test_export(self, mock_print, mock_open, mock_open_new_tab, mock_path): # file self.fake_api.task.export.return_value = { "files": {"output_dest": "content"}, "open": "output_dest"} mock_path.exists.side_effect = [False, True, False] mock_path.expanduser.return_value = "output_file" mock_path.realpath.return_value = "real_path" mock_fd = mock.mock_open() mock_open.side_effect = mock_fd self.task._load_task_results_file = mock.MagicMock( return_value=[{"task": "task_1"}, {"task": "task2"}]) self.task.export(self.fake_api, tasks=["uuid", "file"], output_type="json", output_dest="output_dest", open_it=True) self.fake_api.task.export.assert_called_once_with( tasks=["uuid"] + self.task._load_task_results_file.return_value, output_type="json", output_dest="output_dest" ) mock_open.assert_called_once_with("output_file", "w+") mock_fd.return_value.write.assert_called_once_with("content") # print self.fake_api.task.export.reset_mock() self.fake_api.task.export.return_value = {"print": "content"} self.task.export(self.fake_api, tasks="uuid", output_type="json") self.fake_api.task.export.assert_called_once_with( tasks=["uuid"], output_type="json", output_dest=None ) mock_print.assert_called_once_with("content") @mock.patch("rally.cli.commands.task.sys.stdout") @ddt.data({"error_type": "test_no_trace_type", "error_message": "no_trace_error_message", "error_traceback": None, }, {"error_type": "test_error_type", "error_message": "test_error_message", "error_traceback": "test\nerror\ntraceback", }) @ddt.unpack def test_show_task_errors_no_trace(self, mock_stdout, error_type, error_message, error_traceback=None): test_uuid = "test_task_id" error_data = [error_type, error_message] if error_traceback: error_data.append(error_traceback) self.fake_api.task.get.return_value = { "id": "task", "uuid": test_uuid, "status": "finished", "pass_sla": True, "subtasks": [{"workloads": [{ "name": "fake_name", "position": "fake_pos", "args": {}, "runner_type": "foo", "runner": {}, "contexts": {}, "sla": {}, "hooks": {}, "load_duration": 3.2, "full_duration": 3.5, "statistics": { "durations": { "atomics": [ { "name": "foo", "display_name": "foo (x2)", "count_per_iteration": 2, "children": [], "data": { "min": 1, "median": 2, "90%ile": 1.5, "95%ile": 1.6, "max": 3, "avg": 1.4, "success": 3, "iteration_count": 3}}, { "name": "bar", "display_name": "bar", "count_per_iteration": 1, "children": [], "data": { "min": 1.1, "median": 2.2, "90%ile": 1.6, "95%ile": 1.65, "max": 3, "avg": 1.5, "success": 3, "iteration_count": 3} }], "total": { "name": "total", "display_name": "bar", "count_per_iteration": 1, "children": [], "data": { "min": 1, "median": 2.1, "90%ile": 1.55, "95%ile": 1.62, "max": 3, "avg": 1.45, "success": 6, "iteration_count": 6}}} }, "total_iteration_count": 1, "total_iteration_failed": 1, "data": [ {"duration": 0.9, "idle_duration": 0.1, "output": {"additive": [], "complete": []}, "atomic_actions": {"foo": 0.6, "bar": 0.7}, "error": error_data }, ]}, ]}], "validation_result": json.dumps([error_type, error_message, error_traceback]) } self.task.detailed(self.fake_api, test_uuid) self.fake_api.task.get.assert_called_once_with( task_id=test_uuid, detailed=True) mock_stdout.write.assert_has_calls([ mock.call(error_traceback or "No traceback available.") ], any_order=False) @mock.patch("rally.cli.commands.task.open", create=True) @mock.patch("rally.cli.commands.task.json.loads") @mock.patch("rally.cli.commands.task.jsonschema.validate", return_value=None) def test__load_task_results_file(self, mock_validate, mock_loads, mock_open): task_file = "/tmp/task.json" workload = { "uuid": "n/a", "full_duration": 2, "load_duration": 1, "created_at": "2017-07-01T07:03:01", "updated_at": "2017-07-01T07:03:03", "total_iteration_count": 2, "failed_iteration_count": 1, "min_duration": 3, "max_duration": 5, "start_time": 1, "name": "Foo.bar", "description": "descr", "position": 2, "args": {"key1": "value1"}, "runner_type": "constant", "runner": {"time": 3}, "hooks": [{"config": { "description": "descr", "action": ("foo", {"arg1": "v1"}), "trigger": ("t", {"a2", "v2"})}}], "pass_sla": True, "sla": {"failure_rate": {"max": 0}}, "sla_results": {"sla": [{"success": True}]}, "contexts": {"users": {}}, "contexts_results": [], "data": [{"timestamp": 1, "atomic_actions": {"foo": 1.0, "bar": 1.0}, "duration": 5, "idle_duration": 0, "error": [{}]}, {"timestamp": 2, "atomic_actions": {"bar": 1.1}, "duration": 3, "idle_duration": 0, "error": []}], "statistics": {"durations": mock.ANY} } results = [{ "hooks": [{ "config": { "name": "foo", "args": {"arg1": "v1"}, "description": "descr", "trigger": {"name": "t", "args": {"a2", "v2"}}}}], "key": { "name": workload["name"], "description": workload["description"], "pos": workload["position"], "kw": { "args": workload["args"], "runner": {"type": "constant", "time": 3}, "hooks": [{ "name": "foo", "args": {"arg1": "v1"}, "description": "descr", "trigger": {"name": "t", "args": {"a2", "v2"}}}], "sla": workload["sla"], "context": workload["contexts"]}}, "sla": workload["sla_results"]["sla"], "result": workload["data"], "full_duration": workload["full_duration"], "load_duration": workload["load_duration"], "created_at": "2017-01-07T07:03:01"} ] mock_loads.return_value = results ret = self.task._load_task_results_file(self.fake_api, task_file) self.assertEqual([{ "version": 2, "title": "Task loaded from a file.", "description": "Auto-ported from task format V1.", "uuid": "n/a", "env_uuid": "n/a", "env_name": "n/a", "status": "finished", "tags": [], "subtasks": [{ "title": "A SubTask", "description": "", "workloads": [workload]}]}], ret) @mock.patch("rally.cli.commands.task.open", create=True) @mock.patch("rally.cli.commands.task.json.loads") @mock.patch("rally.cli.commands.task.jsonschema.validate") def test__load_task_new_results_file(self, mock_validate, mock_loads, mock_open): task_file = "/tmp/task.json" results = { "tasks": [{ "env_uuid": "env-uuid-1", "env_name": "env-name-1", "subtasks": [{ "workloads": [{ "contexts": "contexts", "scenario": {"Foo.bar": {}}, "runner": {"constant": { "times": 100, "concurrency": 5 }} }] }] }] } mock_loads.return_value = results ret = self.task._load_task_results_file(self.fake_api, task_file) self.assertEqual([{ "env_uuid": "env-uuid-1", "env_name": "env-name-1", "subtasks": [{ "workloads": [{ "args": {}, "name": "Foo.bar", "contexts": "contexts", "contexts_results": [], "runner_type": "constant", "runner": { "times": 100, "concurrency": 5 } }] }] }], ret) @mock.patch("rally.cli.commands.task.open", create=True) @mock.patch("rally.cli.commands.task.json.loads") def test__load_task_results_file_wrong_format(self, mock_loads, mock_open): task_id = "/tmp/task.json" mock_loads.return_value = "results" self.assertRaises(task.FailedToLoadResults, self.task._load_task_results_file, api=self.real_api, task_id=task_id) mock_loads.return_value = ["results"] self.assertRaises(task.FailedToLoadResults, self.task._load_task_results_file, api=self.real_api, task_id=task_id) @mock.patch("rally.cli.commands.task.os.path") def test_import_results(self, mock_os_path): mock_os_path.exists.return_value = True mock_os_path.expanduser = lambda path: path self.task._load_task_results_file = mock.MagicMock( return_value=["results"] ) self.task.import_results(self.fake_api, "deployment_uuid", "task_file", tags=["tag"]) self.task._load_task_results_file.assert_called_once_with( self.fake_api, "task_file" ) self.fake_api.task.import_results.assert_called_once_with( deployment="deployment_uuid", task_results="results", tags=["tag"]) # not exist mock_os_path.exists.return_value = False self.assertEqual( 1, self.task.import_results(self.fake_api, "deployment_uuid", "task_file", ["tag"]) )