add 'rally task list' filters
Add ability to filter tasks by status and/or deployment. Change-Id: I663bddf73a4d5be1b24e712ddcde9efdf494f005
This commit is contained in:
parent
1a302df237
commit
e9f5ceeb33
@ -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)
|
||||
|
@ -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"
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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"),
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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)
|
||||
|
@ -1313,3 +1313,9 @@ class FakeUserContext(FakeContext):
|
||||
|
||||
class FakeDeployment(dict):
|
||||
update_status = mock.Mock()
|
||||
|
||||
|
||||
class FakeTask(dict):
|
||||
|
||||
def to_dict(self):
|
||||
return self
|
||||
|
@ -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):
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user