diff --git a/etc/rally.bash_completion b/etc/rally.bash_completion index 565c8040ec..8eedbe41a0 100644 --- a/etc/rally.bash_completion +++ b/etc/rally.bash_completion @@ -29,7 +29,7 @@ _rally() OPTS["task_abort"]="--uuid" OPTS["task_delete"]="--force --uuid" OPTS["task_detailed"]="--uuid --iterations-data" - OPTS["task_list"]="--deployment --all-deployments --status" + OPTS["task_list"]="--deployment --all-deployments --status --uuids-only" OPTS["task_report"]="--tasks --out --open" OPTS["task_results"]="--uuid" OPTS["task_sla_check"]="--uuid --json" @@ -79,4 +79,4 @@ _rally() fi return 0 } -complete -F _rally rally +complete -F _rally rally \ No newline at end of file diff --git a/rally/cmd/cliutils.py b/rally/cmd/cliutils.py index 237b7f2d02..c2184c9338 100644 --- a/rally/cmd/cliutils.py +++ b/rally/cmd/cliutils.py @@ -84,7 +84,9 @@ def validate_args(fn, *args, **kwargs): def print_list(objs, fields, formatters=None, sortby_index=0, - mixed_case_fields=None, field_labels=None): + mixed_case_fields=None, field_labels=None, + print_header=True, print_border=True, + out=sys.stdout): """Print a list or objects as a table, one row per object. :param objs: iterable of :class:`Resource` @@ -95,6 +97,9 @@ def print_list(objs, fields, formatters=None, sortby_index=0, have mixed case names (e.g., 'serverId') :param field_labels: Labels to use in the heading of the table, default to fields. + :param print_header: print table header. + :param print_border: print table border. + :param out: stream to write output to. """ formatters = formatters or {} mixed_case_fields = mixed_case_fields or [] @@ -125,10 +130,19 @@ def print_list(objs, fields, formatters=None, sortby_index=0, row.append(data) pt.add_row(row) + if not print_border or not print_header: + pt.set_style(prettytable.PLAIN_COLUMNS) + pt.left_padding_width = 0 + pt.right_padding_width = 1 + + outstr = pt.get_string(header=print_header, + border=print_border, + **kwargs) + "\n" + if six.PY3: - print(encodeutils.safe_encode(pt.get_string(**kwargs)).decode()) + out.write(encodeutils.safe_encode(outstr).decode()) else: - print(encodeutils.safe_encode(pt.get_string(**kwargs))) + out.write(encodeutils.safe_encode(outstr)) def make_header(text, size=80, symbol="-"): diff --git a/rally/cmd/commands/task.py b/rally/cmd/commands/task.py index 0068c905a0..d52bdd8696 100644 --- a/rally/cmd/commands/task.py +++ b/rally/cmd/commands/task.py @@ -443,8 +443,11 @@ class TaskCommands(object): @cliutils.args("--status", type=str, dest="status", help="List tasks with specified status." " Available statuses: %s" % ", ".join(consts.TaskStatus)) + @cliutils.args("--uuids-only", action="store_true", + dest="uuids_only", help="List task UUIDs only") @envutils.with_default_deployment(cli_arg_name="deployment") - def list(self, deployment=None, all_deployments=False, status=None): + def list(self, deployment=None, all_deployments=False, status=None, + uuids_only=False): """List tasks, started and finished. Displayed tasks could be filtered by status or deployment. @@ -454,6 +457,7 @@ class TaskCommands(object): :param status: task status to filter by. Available task statuses are in rally.consts.TaskStatus :param all_deployments: display tasks from all deployments + :param uuids_only: list task UUIDs only """ filters = {} @@ -477,7 +481,12 @@ class TaskCommands(object): for x in task_list: x["duration"] = x["updated_at"] - x["created_at"] - if task_list: + if uuids_only: + if task_list: + cliutils.print_list(task_list, ["uuid"], + print_header=False, + print_border=False) + elif task_list: cliutils.print_list( task_list, headers, sortby_index=headers.index("created_at")) diff --git a/tests/functional/test_cli_task.py b/tests/functional/test_cli_task.py index 2bd1feb3f7..f42b45a625 100644 --- a/tests/functional/test_cli_task.py +++ b/tests/functional/test_cli_task.py @@ -231,6 +231,36 @@ class TaskTestCase(unittest.TestCase): self.assertRaises(utils.RallyCmdError, rally, "task list --status not_existing_status") + def test_list_with_print_uuids_option(self): + rally = utils.Rally() + cfg = self._get_sample_task_config() + config = utils.TaskConfig(cfg) + + # Validate against zero tasks + self.assertEqual("", rally("task list --uuids-only")) + + # Validate against a single task + res = rally("task start --task %s" % config.filename) + task_uuids = list() + for line in res.splitlines(): + if "finished" in line: + task_uuids.append(line.split(" ")[1][:-1]) + self.assertTrue(len(task_uuids)) + self.assertIn(task_uuids[0], + rally("task list --uuids-only --deployment MAIN")) + + # Validate against multiple tasks + for i in range(2): + rally("task start --task %s" % config.filename) + self.assertIn("finished", rally("task list --deployment MAIN")) + res = rally("task list --uuids-only --deployment MAIN") + task_uuids = res.split() + self.assertEqual(3, len(task_uuids)) + res = rally("task list --uuids-only --deployment MAIN " + "--status finished") + for uuid in task_uuids: + self.assertIn(uuid, res) + def test_validate_is_valid(self): rally = utils.Rally() cfg = self._get_sample_task_config() diff --git a/tests/unit/cmd/commands/test_task.py b/tests/unit/cmd/commands/test_task.py index 42d910cd42..5edd78e74e 100644 --- a/tests/unit/cmd/commands/test_task.py +++ b/tests/unit/cmd/commands/test_task.py @@ -504,6 +504,26 @@ class TaskCommandsTestCase(test.TestCase): mock_objects_list.return_value, headers, sortby_index=headers.index("created_at")) + @mock.patch("rally.cmd.commands.task.cliutils.print_list") + @mock.patch("rally.cmd.commands.task.envutils.get_global", + return_value="123456789") + @mock.patch("rally.cmd.commands.task.objects.Task.list", + return_value=[fakes.FakeTask(uuid="a", + created_at=date.datetime.now(), + updated_at=date.datetime.now(), + status="c", + tag="d", + deployment_name="some_name")]) + def test_list_uuids_only(self, mock_objects_list, mock_default, + mock_print_list): + self.task.list(status="running", uuids_only=True) + mock_objects_list.assert_called_once_with( + deployment=mock_default.return_value, + status=consts.TaskStatus.RUNNING) + mock_print_list.assert_called_once_with( + mock_objects_list.return_value, ["uuid"], + print_header=False, print_border=False) + def test_list_wrong_status(self): self.assertEqual(1, self.task.list(deployment="fake", status="wrong non existing status")) diff --git a/tests/unit/cmd/test_cliutils.py b/tests/unit/cmd/test_cliutils.py index 3cd40a6e93..e5b9226d7c 100644 --- a/tests/unit/cmd/test_cliutils.py +++ b/tests/unit/cmd/test_cliutils.py @@ -15,6 +15,7 @@ import mock from oslo_config import cfg +from six import moves from rally.cmd import cliutils from rally.cmd.commands import deployment @@ -170,6 +171,171 @@ class CliUtilsTestCase(test.TestCase): {"failure": FailuresCommands}) self.assertEqual(1, ret) + def test_print_list(self): + class TestObj(object): + x = 1 + y = 2 + z = 3.142857142857143 + aOrB = 3 # mixed case field + + out = moves.StringIO() + cliutils.print_list([TestObj()], ["x", "y"], + print_header=True, + print_border=True, + sortby_index=None, + out=out) + self.assertEqual("+---+---+\n" + "| x | y |\n" + "+---+---+\n" + "| 1 | 2 |\n" + "+---+---+", + out.getvalue().strip()) + + out = moves.StringIO() + formatter = cliutils.pretty_float_formatter("z", 5) + cliutils.print_list([TestObj()], ["z"], + print_header=True, + print_border=True, + sortby_index=None, + formatters={"z": formatter}, + out=out) + self.assertEqual("+---------+\n" + "| z |\n" + "+---------+\n" + "| 3.14286 |\n" + "+---------+", + out.getvalue().strip()) + + out = moves.StringIO() + cliutils.print_list([TestObj()], ["x"], + print_header=True, + print_border=True, + out=out) + self.assertEqual("+---+\n" + "| x |\n" + "+---+\n" + "| 1 |\n" + "+---+", + out.getvalue().strip()) + + out = moves.StringIO() + cliutils.print_list([TestObj()], ["x", "y"], + print_header=True, + print_border=True, + out=out) + self.assertEqual("+---+---+\n" + "| x | y |\n" + "+---+---+\n" + "| 1 | 2 |\n" + "+---+---+", + out.getvalue().strip()) + + out = moves.StringIO() + cliutils.print_list([TestObj()], ["x"], + print_header=False, + print_border=False, + out=out) + self.assertEqual("1", + out.getvalue().strip()) + + out = moves.StringIO() + cliutils.print_list([TestObj()], ["x", "y"], + print_header=False, + print_border=False, + out=out) + self.assertEqual("1 2", + out.getvalue().strip()) + + out = moves.StringIO() + cliutils.print_list([TestObj()], ["x"], + print_header=True, + print_border=False, + out=out) + self.assertEqual("x \n1", + out.getvalue().strip()) + + out = moves.StringIO() + cliutils.print_list([TestObj()], ["x", "y"], + print_header=True, + print_border=False, + out=out) + self.assertEqual("x y \n1 2", + out.getvalue().strip()) + + out = moves.StringIO() + cliutils.print_list([TestObj()], ["x"], + print_header=False, + print_border=True, + out=out) + self.assertEqual("+--+\n" + "|1 |\n" + "+--+", + out.getvalue().strip()) + + out = moves.StringIO() + cliutils.print_list([TestObj()], ["x", "y"], + print_header=False, + print_border=True, + out=out) + self.assertEqual("+--+--+\n" + "|1 |2 |\n" + "+--+--+", + out.getvalue().strip()) + + out = moves.StringIO() + cliutils.print_list([TestObj()], ["aOrB"], + mixed_case_fields=["aOrB"], + print_header=True, + print_border=True, + out=out) + self.assertEqual("+------+\n" + "| aOrB |\n" + "+------+\n" + "| 3 |\n" + "+------+", + out.getvalue().strip()) + + out = moves.StringIO() + cliutils.print_list([TestObj()], ["aOrB"], + mixed_case_fields=["aOrB"], + print_header=False, + print_border=True, + out=out) + self.assertEqual("+--+\n" + "|3 |\n" + "+--+", + out.getvalue().strip()) + + out = moves.StringIO() + cliutils.print_list([TestObj()], ["aOrB"], + mixed_case_fields=["aOrB"], + print_header=True, + print_border=False, + out=out) + self.assertEqual("aOrB \n" + "3", + out.getvalue().strip()) + + out = moves.StringIO() + cliutils.print_list([TestObj()], ["aOrB"], + mixed_case_fields=["aOrB"], + print_header=False, + print_border=False, + out=out) + self.assertEqual("3", + out.getvalue().strip()) + + out = moves.StringIO() + self.assertRaisesRegexp(ValueError, + "Field labels list.*has different number " + "of elements than fields list", + cliutils.print_list, + [TestObj()], + ["x"], + field_labels=["x", "y"], + sortby_index=None, + out=out) + class ValidateArgsTest(test.TestCase):