add 'rally task list' filters

Add ability to filter tasks by status and/or deployment.

Change-Id: I663bddf73a4d5be1b24e712ddcde9efdf494f005
This commit is contained in:
temujin 2014-10-24 17:42:29 +03:00 committed by Oleh Anufriiev
parent 1a302df237
commit e9f5ceeb33
12 changed files with 150 additions and 44 deletions

View File

@ -22,6 +22,7 @@ import sys
from oslo.config import cfg
from rally import exceptions
from rally.i18n import _
from rally import log as logging
from rally.openstack.common import cliutils
@ -296,7 +297,7 @@ def run(argv, categories):
validate_deprecated_args(argv, fn)
ret = fn(*fn_args, **fn_kwargs)
return(ret)
except (IOError, TypeError) as e:
except (IOError, TypeError, exceptions.DeploymentNotFound) as e:
if CONF.debug:
raise
print(e)

View File

@ -29,10 +29,11 @@ from rally.benchmark.processing import utils
from rally.cmd import cliutils
from rally.cmd.commands import use
from rally.cmd import envutils
from rally import consts
from rally import db
from rally import exceptions
from rally.i18n import _
from rally.objects import task
from rally import objects
from rally.openstack.common import cliutils as common_cliutils
from rally.orchestrator import api
from rally import utils as rutils
@ -322,7 +323,7 @@ class TaskCommands(object):
results = map(lambda x: {"key": x["key"], "result": x["data"]["raw"],
"sla": x["data"]["sla"]},
task.Task.get(task_id).get_results())
objects.Task.get(task_id).get_results())
if results:
print(json.dumps(results, sort_keys=True, indent=4))
@ -330,18 +331,58 @@ class TaskCommands(object):
print(_("The task %s can not be found") % task_id)
return(1)
def list(self, task_list=None):
"""List all tasks, started and finished."""
@cliutils.args('--deployment', type=str, dest='deployment',
help='List tasks from specified deployment.'
'By default tasks listed from active deployment.')
@cliutils.args('--all-deployments', action='store_true',
dest='all_deployments',
help='List tasks from all deployments.')
@cliutils.args('--status', type=str, dest='status',
help='List tasks with specified status.'
' Available statuses: %s' % ', '.join(consts.TaskStatus))
@envutils.with_default_deployment
def list(self, deployment=None, all_deployments=False, status=None):
"""List tasks, started and finished.
Displayed tasks could be filtered by status or deployment.
By default 'rally task list' will display tasks from active deployment
without filtering by status.
:param deployment: UUID or name of deployment
:param status: task status to filter by.
Available task statuses are in rally.consts.TaskStatus
:param all_deployments: display tasks from all deployments
"""
filters = dict()
headers = ["uuid", "deployment_name", "created_at", "status",
"failed", "tag"]
if status in consts.TaskStatus:
filters.setdefault("status", status)
elif status is not None:
print(_("Error: Invalid task status '%s'.\n"
"Available statuses: %s") % (
status, ", ".join(consts.TaskStatus)))
return(1)
if not all_deployments:
filters.setdefault("deployment", deployment)
task_list = objects.Task.list(**filters)
headers = ['uuid', 'created_at', 'status', 'failed', 'tag']
task_list = task_list or db.task_list()
if task_list:
common_cliutils.print_list(task_list, headers,
common_cliutils.print_list(map(lambda x: x.to_dict(), task_list),
headers,
sortby_index=headers.index(
'created_at'))
else:
print(_("There are no tasks. To run a new task, use:"
"\nrally task start"))
if status:
print(_("There are no tasks in '%s' status. "
"To run a new task, use:"
"\trally task start") % status)
else:
print(_("There are no tasks. To run a new task, use:"
"\trally task start"))
@cliutils.args('--uuid', type=str, dest='task_id', help='uuid of task')
@cliutils.args('--out', type=str, dest='out', required=False,
@ -361,7 +402,7 @@ class TaskCommands(object):
"result": x["data"]["raw"],
"load_duration": x["data"]["load_duration"],
"full_duration": x["data"]["full_duration"]},
task.Task.get(task_id).get_results())
objects.Task.get(task_id).get_results())
if out:
out = os.path.expanduser(out)
output_file = out or ("%s.html" % task_id)
@ -413,7 +454,7 @@ class TaskCommands(object):
:param task_id: Task uuid.
:returns: Number of failed criteria.
"""
results = task.Task.get(task_id).get_results()
results = objects.Task.get(task_id).get_results()
failed_criteria = 0
data = []
STATUS_PASS = "PASS"

View File

@ -114,14 +114,17 @@ def task_update(uuid, values):
return IMPL.task_update(uuid, values)
def task_list(status=None):
def task_list(status=None, deployment=None):
"""Get a list of tasks.
:param status: Task status to filter the returned list on. If set to
None, all the tasks will be returned.
:param deployment_id: deployment UUID to filter the returned list on.
if set to None tasks from all deployments well be
returned.
:returns: A list of dicts with data on the tasks.
"""
return IMPL.task_list(status=status)
return IMPL.task_list(status=status, deployment=deployment)
def task_delete(uuid, status=None):

View File

@ -127,10 +127,18 @@ class Connection(object):
task.update(values)
return task
def task_list(self, status=None):
def task_list(self, status=None, deployment=None):
query = self.model_query(models.Task)
filters = {}
if status is not None:
query = query.filter_by(status=status)
filters['status'] = status
if deployment is not None:
filters['deployment_uuid'] = self.deployment_get(
deployment)["uuid"]
if filters:
query = query.filter_by(**filters)
return query.all()
def task_delete(self, uuid, status=None):

View File

@ -136,6 +136,8 @@ class Task(BASE, RallyBase):
__tablename__ = "tasks"
__table_args__ = (
sa.Index("task_uuid", "uuid", unique=True),
sa.Index("task_status", "status"),
sa.Index("task_deployment", "deployment_uuid"),
)
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
@ -153,6 +155,7 @@ class Task(BASE, RallyBase):
sa.ForeignKey(Deployment.uuid),
nullable=False,
)
deployment = sa.orm.relationship(
Deployment,
backref=sa.orm.backref("tasks"),

View File

@ -31,10 +31,20 @@ class Task(object):
def __getitem__(self, key):
return self.task[key]
def to_dict(self):
db_task = self.task
deployment_name = db.deployment_get(self.task.deployment_uuid)["name"]
db_task["deployment_name"] = deployment_name
return db_task
@staticmethod
def get(uuid):
return Task(db.task_get(uuid))
@staticmethod
def list(status=None, deployment=None):
return [Task(db_task) for db_task in db.task_list(status, deployment)]
@staticmethod
def delete_by_uuid(uuid, status=None):
db.task_delete(uuid, status=status)

View File

@ -81,6 +81,19 @@ class TaskTestCase(unittest.TestCase):
rally("task delete")
self.assertNotIn("finishe", rally("task list"))
def test_list(self):
rally = utils.Rally()
cfg = self._get_sample_task_config()
config = utils.TaskConfig(cfg)
rally("task start --task %s" % config.filename)
self.assertIn("finished", rally("task list --deployment MAIN"))
self.assertIn("There are no tasks",
rally("task list --status failed"))
self.assertIn("finished", rally("task list --status finished"))
self.assertIn("deployment_name", rally("task list --all-deployments"))
self.assertRaises(utils.RallyCmdError,
rally, "task list --status not_existing_status")
# NOTE(oanufriev): Not implemented
def test_abort(self):
pass

View File

@ -17,6 +17,7 @@ import mock
from rally.cmd.commands import task
from rally import exceptions
from tests.unit import fakes
from tests.unit import test
@ -135,7 +136,7 @@ class TaskCommandsTestCase(test.TestCase):
mock_db.task_get_detailed.assert_called_once_with(test_uuid)
@mock.patch("json.dumps")
@mock.patch("rally.cmd.commands.task.task.Task.get")
@mock.patch("rally.cmd.commands.task.objects.Task.get")
def test_results(self, mock_get, mock_json):
task_id = "foo_task_id"
data = [
@ -151,7 +152,7 @@ class TaskCommandsTestCase(test.TestCase):
mock_json.assert_called_once_with(result, sort_keys=True, indent=4)
mock_get.assert_called_once_with(task_id)
@mock.patch("rally.cmd.commands.task.task.Task.get")
@mock.patch("rally.cmd.commands.task.objects.Task.get")
def test_invalid_results(self, mock_get):
task_id = "foo_task_id"
data = []
@ -167,7 +168,7 @@ class TaskCommandsTestCase(test.TestCase):
@mock.patch("rally.cmd.commands.task.open", create=True)
@mock.patch("rally.cmd.commands.task.plot")
@mock.patch("rally.cmd.commands.task.webbrowser")
@mock.patch("rally.cmd.commands.task.task.Task.get")
@mock.patch("rally.cmd.commands.task.objects.Task.get")
def test_report(self, mock_get, mock_web, mock_plot, mock_open, mock_os):
task_id = "foo_task_id"
data = [
@ -213,28 +214,26 @@ class TaskCommandsTestCase(test.TestCase):
"file://realpath_spam.html")
@mock.patch('rally.cmd.commands.task.common_cliutils.print_list')
@mock.patch('rally.cmd.commands.task.envutils.get_global')
@mock.patch("rally.cmd.commands.task.db")
def test_list(self, mock_db, mock_default, mock_print_list):
mock_default.side_effect = exceptions.InvalidArgumentsException
self.assertRaises(exceptions.InvalidArgumentsException,
self.task.results, None)
@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="b",
status="c",
failed=True,
tag="d",
deployment_name="some_name")])
def test_list(self, mock_objects_list, mock_default, mock_print_list):
db_response = [
{'uuid': 'a',
'created_at': 'b',
'status': 'c',
'failed': True,
'tag': 'd'}
]
mock_db.task_list = mock.MagicMock(return_value=db_response)
self.task.list()
mock_db.task_list.assert_called_once_with()
mock_objects_list.assert_called_once_with(
deployment=mock_default.return_value)
headers = ['uuid', 'created_at', 'status', 'failed', 'tag']
mock_print_list.assert_called_once_with(db_response, headers,
sortby_index=headers.index(
'created_at'))
headers = ["uuid", "deployment_name", "created_at", "status",
"failed", "tag"]
mock_print_list.assert_called_once_with(
mock_objects_list.return_value, headers,
sortby_index=headers.index('created_at'))
def test_delete(self):
task_uuid = '8dcb9c5e-d60b-4022-8975-b5987c7833f7'

View File

@ -14,6 +14,7 @@
# under the License.
"""Tests for db.api layer."""
import uuid
from rally import consts
from rally import db
@ -75,22 +76,28 @@ class TasksTestCase(test.DBTestCase):
self.assertEqual([], db.task_list())
def test_task_list(self):
fake_deploy_uuid = str(uuid.uuid4())
INIT = consts.TaskStatus.INIT
task_init = sorted(self._create_task()['uuid'] for i in xrange(3))
FINISHED = consts.TaskStatus.FINISHED
task_finished = sorted(self._create_task({'status': FINISHED})['uuid']
task_finished = sorted(self._create_task(
{'status': FINISHED,
'deployment_uuid': fake_deploy_uuid})[
'uuid']
for i in xrange(3))
task_all = sorted(task_init + task_finished)
def get_uuids(status):
tasks = db.task_list(status=status)
def get_uuids(status=None, deployment=None):
tasks = db.task_list(status=status, deployment=deployment)
return sorted(task['uuid'] for task in tasks)
self.assertEqual(task_all, get_uuids(None))
self.assertEqual(task_init, get_uuids(INIT))
self.assertEqual(task_finished, get_uuids(FINISHED))
self.assertEqual(task_init, get_uuids(status=INIT))
self.assertEqual(task_finished, get_uuids(status=FINISHED))
self.assertRaises(exceptions.DeploymentNotFound,
get_uuids, deployment=fake_deploy_uuid)
deleted_task_uuid = task_finished.pop()
db.task_delete(deleted_task_uuid)

View File

@ -1313,3 +1313,9 @@ class FakeUserContext(FakeContext):
class FakeDeployment(dict):
update_status = mock.Mock()
class FakeTask(dict):
def to_dict(self):
return self

View File

@ -83,6 +83,21 @@ class TaskTestCase(test.TestCase):
mock_delete.assert_called_once_with(self.task['uuid'],
status=consts.TaskStatus.FINISHED)
@mock.patch("rally.objects.task.db.task_list",
return_value=[{"uuid": "a",
"created_at": "b",
"status": "c",
"failed": True,
"tag": "d",
"deployment_name": "some_name"}])
def list(self, mock_db_task_list):
tasks = objects.Task.list(status="somestatus")
mock_db_task_list.assert_called_once_with("somestatus", None)
self.assertIs(type(tasks), list)
self.assertIsInstance(tasks[0], objects.Task)
self.assertEqual(mock_db_task_list.return_value["uuis"],
tasks[0]["uuid"])
@mock.patch('rally.objects.deploy.db.task_update')
@mock.patch('rally.objects.task.db.task_create')
def test_update(self, mock_create, mock_update):

View File

@ -19,7 +19,7 @@ _rally()
OPTS["task_abort"]="--uuid"
OPTS["task_delete"]="--force --uuid"
OPTS["task_detailed"]="--uuid --iterations-data"
OPTS["task_list"]=""
OPTS["task_list"]="--deployment --all-deployments --status"
OPTS["task_plot2html"]="--uuid --out --open"
OPTS["task_report"]="--uuid --out --open"
OPTS["task_results"]="--uuid"