From 3530a858e37bcb2d690695df2d814a7ff226c965 Mon Sep 17 00:00:00 2001 From: Dennis Kliban Date: Tue, 3 Nov 2015 16:50:54 +0000 Subject: [PATCH] Adds task groups status API endpoint https://pulp.plan.io/issues/1206 closes #1206 --- docs/dev-guide/integration/rest-api/tasks.rst | 56 +++++++++++++++ server/pulp/server/webservices/urls.py | 4 +- .../server/webservices/views/task_groups.py | 43 +++++++++++ .../webservices/views/test_task_groups.py | 71 +++++++++++++++++++ 4 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 server/pulp/server/webservices/views/task_groups.py create mode 100644 server/test/unit/server/webservices/views/test_task_groups.py diff --git a/docs/dev-guide/integration/rest-api/tasks.rst b/docs/dev-guide/integration/rest-api/tasks.rst index 87cfa0d481..2b5d1c8ad5 100644 --- a/docs/dev-guide/integration/rest-api/tasks.rst +++ b/docs/dev-guide/integration/rest-api/tasks.rst @@ -59,6 +59,7 @@ Example Task Report:: } + Polling Task Progress --------------------- @@ -120,6 +121,7 @@ All currently running and waiting tasks may be listed. This returns an array of | :return:`array of` :ref:`task_report` + Deleting Completed Tasks ------------------------ @@ -174,3 +176,57 @@ For example:: | :response_list:`_` * :response_code:`200,containing the array of tasks.` + +.. _task_group_management: + +Task Group Management +===================== + +.. _task_group_summary: + +Task Group Summary +------------------ + +Task Group Summary object summarizes the state of all the tasks belonging to a task group. + + * **accepted** *(int)* - number of tasks in 'accepted' state + * **finished** *(int)* - number of tasks in 'finished' state + * **running** *(int)* - number of tasks in 'running' state + * **canceled** *(int)* - number of tasks in 'canceled' state + * **waiting** *(int)* - number of tasks in 'waiting' state + * **skipped** *(int)* - number of tasks in 'skipped' state + * **suspended** *(int)* - number of tasks in 'suspended' state + * **error** *(int)* - number of tasks in 'error' state + * **total** *(int)* - total number of tasks in the task group + +Example task group summary:: + + { + "accepted": 0, + "finished": 100, + "running": 4, + "canceled": 0, + "waiting": 2, + "skipped": 0, + "suspended": 0, + "error": 0, + "total": 106 + } + + +Polling Task Group Progress +---------------------------- + +Poll a group of tasks for progress summary. Polling returns a :ref:`task_group_summary` + +| :method:`get` +| :path:`/v2/task_groups//state_summary/` +| :permission:`read` + +| :response_list:`_` + +* :response_code:`200, if the task group is found` +* :response_code:`404, if the task group id is not found` + +| :return:`a` :ref:`task_group_summary` summarizing the state of all tasks belonging to + queried task group id diff --git a/server/pulp/server/webservices/urls.py b/server/pulp/server/webservices/urls.py index c589760d6c..ab046eadef 100644 --- a/server/pulp/server/webservices/urls.py +++ b/server/pulp/server/webservices/urls.py @@ -21,7 +21,7 @@ UnitUpdateSchedulesView, UnitUpdateScheduleResourceView) -from pulp.server.webservices.views import tasks, users +from pulp.server.webservices.views import tasks, users, task_groups from pulp.server.webservices.views.consumer_groups import (ConsumerGroupAssociateActionView, ConsumerGroupBindingView, ConsumerGroupBindingsView, @@ -244,6 +244,8 @@ url(r'^v2/tasks/$', tasks.TaskCollectionView.as_view(), name='task_collection'), url(r'^v2/tasks/search/$', tasks.TaskSearchView.as_view(), name='task_search'), url(r'^v2/tasks/(?P[^/]+)/$', tasks.TaskResourceView.as_view(), name='task_resource'), + url(r'^v2/task_groups/(?P[^/]+)/state_summary/$', + task_groups.TaskGroupSummaryView.as_view(), name='task_group_summary'), url(r'^v2/users/$', users.UsersView.as_view(), name='users'), url(r'^v2/users/search/$', users.UserSearchView.as_view(), name='user_search'), diff --git a/server/pulp/server/webservices/views/task_groups.py b/server/pulp/server/webservices/views/task_groups.py new file mode 100644 index 0000000000..c81026e0ab --- /dev/null +++ b/server/pulp/server/webservices/views/task_groups.py @@ -0,0 +1,43 @@ +""" +This module contains views related to Pulp's task groups. +""" +from django.views.generic import View + +from pulp.common.constants import CALL_STATES +from pulp.server.auth import authorization +from pulp.server.db.model import TaskStatus +from pulp.server.exceptions import MissingResource +from pulp.server.webservices.views.decorators import auth_required +from pulp.server.webservices.views.util import generate_json_response_with_pulp_encoder + + +class TaskGroupSummaryView(View): + """ + View for a task group summary. + """ + + @auth_required(authorization.READ) + def get(self, request, group_id): + """ + Return a response containing a summary of task states for task group. + + :param request: WSGI request object + :type request: django.core.handlers.wsgi.WSGIRequest + :param group_id: The ID of the task group you wish to summarize + :type group_id: basestring + + :return: Response containing a serialized dict of the task group summary + :rtype : django.http.HttpResponse + :raises MissingResource: if group id is not found + """ + tasks = TaskStatus.objects(group_id=group_id) + task_group_total = tasks.count() + + if task_group_total == 0: + raise MissingResource(group_id) + + summary = {'total': task_group_total} + for state in CALL_STATES: + summary[state] = tasks.filter(state=state).count() + + return generate_json_response_with_pulp_encoder(summary) diff --git a/server/test/unit/server/webservices/views/test_task_groups.py b/server/test/unit/server/webservices/views/test_task_groups.py new file mode 100644 index 0000000000..2f0b0e4d68 --- /dev/null +++ b/server/test/unit/server/webservices/views/test_task_groups.py @@ -0,0 +1,71 @@ +""" +This module contains tests for the pulp.server.webservices.views.task_groups module. +""" +import mock + +from .base import assert_auth_READ +from pulp.common.compat import unittest + +from pulp.server.exceptions import MissingResource +from pulp.server.webservices.views.task_groups import TaskGroupSummaryView + + +class TestTaskGroupSummary(unittest.TestCase): + """ + Tests for TaskGroupSummaryView + """ + + @mock.patch('pulp.server.webservices.views.decorators._verify_auth', + new=assert_auth_READ()) + @mock.patch('pulp.server.webservices.views.task_groups.TaskStatus.objects') + def test_get_task_group_summary_nonexistant(self, mock_objects): + """ + Test get task_group_summary with no tasks + """ + + mock_request = mock.MagicMock() + mock_objects.return_value.count.return_value = 0 + task_group_summary = TaskGroupSummaryView() + self.assertRaises(MissingResource, task_group_summary.get, mock_request, 'mock_task') + + @mock.patch('pulp.server.webservices.views.decorators._verify_auth', + new=assert_auth_READ()) + @mock.patch('pulp.server.webservices.views.task_groups.TaskStatus.objects') + @mock.patch( + 'pulp.server.webservices.views.task_groups.generate_json_response_with_pulp_encoder') + def test_get_task_group_summary(self, mock_resp, mock_objects): + """ + Test get task_group_summary with multiple tasks + """ + class MockQuerySet(object): + + def __init__(self, list_of_objects): + self.items = list_of_objects + self.i = 0 + self.n = len(self.items) + + def count(self): + return len(self.items) + + def filter(self, state=None): + filtered_list = [] + for item in self.items: + if item['state'] == state: + filtered_list.append(item) + return MockQuerySet(filtered_list) + + mock_request = mock.MagicMock() + mock_objects.return_value = MockQuerySet([{'id': 'mock_task', 'worker_name': 'mock', + 'state': 'running'}, + {'id': 'mock_task', 'worker_name': 'mock', + 'state': 'finished'}, + {'id': 'mock_task', 'worker_name': 'mock', + 'state': 'waiting'}]) + + task_group_summary = TaskGroupSummaryView() + response = task_group_summary.get(mock_request, 'mock_task') + + expected_content = {'accepted': 0, 'finished': 1, 'running': 1, 'canceled': 0, + 'waiting': 1, 'skipped': 0, 'suspended': 0, 'error': 0, 'total': 3} + mock_resp.assert_called_with(expected_content) + self.assertTrue(response is mock_resp.return_value)