From 35bd38d677b247bb6421fbea49fc9b3fb0eced5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s=20Rocha?= Date: Wed, 3 Apr 2013 17:33:11 -0400 Subject: [PATCH] Add status app with simple view to check celery workers --- cms/envs/common.py | 3 + cms/envs/dev.py | 3 + cms/envs/test.py | 2 + cms/urls.py | 6 ++ common/djangoapps/service_status/__init__.py | 3 + common/djangoapps/service_status/tasks.py | 25 +++++++++ common/djangoapps/service_status/test.py | 47 ++++++++++++++++ common/djangoapps/service_status/urls.py | 15 +++++ common/djangoapps/service_status/views.py | 59 ++++++++++++++++++++ lms/envs/common.py | 3 + lms/envs/dev.py | 2 +- lms/envs/test.py | 2 + lms/urls.py | 5 ++ 13 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 common/djangoapps/service_status/__init__.py create mode 100644 common/djangoapps/service_status/tasks.py create mode 100644 common/djangoapps/service_status/test.py create mode 100644 common/djangoapps/service_status/urls.py create mode 100644 common/djangoapps/service_status/views.py diff --git a/cms/envs/common.py b/cms/envs/common.py index fb61db4d73..0aca432654 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -34,6 +34,9 @@ MITX_FEATURES = { 'STAFF_EMAIL': '', # email address for staff (eg to request course creation) 'STUDIO_NPS_SURVEY': True, 'SEGMENT_IO': True, + + # Enable URL that shows information about the status of variuous services + 'ENABLE_SERVICE_STATUS': False, } ENABLE_JASMINE = False diff --git a/cms/envs/dev.py b/cms/envs/dev.py index a05ad5731b..f3080c356f 100644 --- a/cms/envs/dev.py +++ b/cms/envs/dev.py @@ -156,5 +156,8 @@ DEBUG_TOOLBAR_MONGO_STACKTRACES = True # disable NPS survey in dev mode MITX_FEATURES['STUDIO_NPS_SURVEY'] = False +# Enable URL that shows information about the status of variuous services +MITX_FEATURES['ENABLE_SERVICE_STATUS'] = True + # segment-io key for dev SEGMENT_IO_KEY = 'mty8edrrsg' diff --git a/cms/envs/test.py b/cms/envs/test.py index 4362aaaac0..c4d2b2d972 100644 --- a/cms/envs/test.py +++ b/cms/envs/test.py @@ -126,3 +126,5 @@ SEGMENT_IO_KEY = '***REMOVED***' # disable NPS survey in test mode MITX_FEATURES['STUDIO_NPS_SURVEY'] = False + +MITX_FEATURES['ENABLE_SERVICE_STATUS'] = True diff --git a/cms/urls.py b/cms/urls.py index 3b91eceb44..cdeea35cb7 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -135,6 +135,12 @@ if settings.ENABLE_JASMINE: # # Jasmine urlpatterns = urlpatterns + (url(r'^_jasmine/', include('django_jasmine.urls')),) + +if settings.MITX_FEATURES.get('ENABLE_SERVICE_STATUS'): + urlpatterns += ( + url(r'^status/', include('service_status.urls')), + ) + urlpatterns = patterns(*urlpatterns) # Custom error pages diff --git a/common/djangoapps/service_status/__init__.py b/common/djangoapps/service_status/__init__.py new file mode 100644 index 0000000000..e90be0088e --- /dev/null +++ b/common/djangoapps/service_status/__init__.py @@ -0,0 +1,3 @@ +""" +Stub for a Django app to report the status of various services +""" diff --git a/common/djangoapps/service_status/tasks.py b/common/djangoapps/service_status/tasks.py new file mode 100644 index 0000000000..40367120b2 --- /dev/null +++ b/common/djangoapps/service_status/tasks.py @@ -0,0 +1,25 @@ +""" +Django Celery tasks for service status app +""" + +import time + +from dogapi import dog_stats_api + +from djcelery import celery + + +@celery.task +@dog_stats_api.timed('status.service.celery.pong') +def delayed_ping(value, delay): + """A simple tasks that replies to a message after a especified amount + of seconds. + """ + if value == 'ping': + result = 'pong' + else: + result = 'got: {0}'.format(value) + + time.sleep(delay) + + return result diff --git a/common/djangoapps/service_status/test.py b/common/djangoapps/service_status/test.py new file mode 100644 index 0000000000..1c4f10497e --- /dev/null +++ b/common/djangoapps/service_status/test.py @@ -0,0 +1,47 @@ +"""Test for async task service status""" + +from django.utils import unittest +from django.test.client import Client +from django.core.urlresolvers import reverse +import json + + +class CeleryConfigTest(unittest.TestCase): + """ + Test that we can get a response from Celery + """ + + def setUp(self): + """ + Create a django test client + """ + self.client = Client() + self.ping_url = reverse('status.service.celery.ping') + + def test_ping(self): + """ + Try to ping celery. + """ + + # Access the service status page, which starts a delayed + # asynchronous task + response = self.client.get(self.ping_url) + + # HTTP response should be successful + self.assertEqual(response.status_code, 200) + + # Expect to get a JSON-serialized dict with + # task and time information + result_dict = json.loads(response.content) + + # Was it successful? + self.assertTrue(result_dict['success']) + + # We should get a "pong" message back + self.assertEqual(result_dict['value'], "pong") + + # We don't know the other dict values exactly, + # but we can assert that they take the right form + self.assertTrue(isinstance(result_dict['task_id'], unicode)) + self.assertTrue(isinstance(result_dict['time'], float)) + self.assertTrue(result_dict['time'] > 0.0) diff --git a/common/djangoapps/service_status/urls.py b/common/djangoapps/service_status/urls.py new file mode 100644 index 0000000000..c5ee44b8b6 --- /dev/null +++ b/common/djangoapps/service_status/urls.py @@ -0,0 +1,15 @@ +""" +Django URLs for service status app +""" + +from django.conf.urls import patterns, url + + +urlpatterns = patterns( + '', + url(r'^$', 'service_status.views.index', name='status.service.index'), + url(r'^celery/$', 'service_status.views.celery_status', + name='status.service.celery.status'), + url(r'^celery/ping/$', 'service_status.views.celery_ping', + name='status.service.celery.ping'), +) diff --git a/common/djangoapps/service_status/views.py b/common/djangoapps/service_status/views.py new file mode 100644 index 0000000000..7233cbdbda --- /dev/null +++ b/common/djangoapps/service_status/views.py @@ -0,0 +1,59 @@ +""" +Django Views for service status app +""" + +import json +import time + +from django.http import HttpResponse + +from dogapi import dog_stats_api + +from service_status import tasks +from djcelery import celery +from celery.exceptions import TimeoutError + + +def index(_): + """ + An empty view + """ + return HttpResponse() + + +@dog_stats_api.timed('status.service.celery.status') +def celery_status(_): + """ + A view that returns Celery stats + """ + stats = celery.control.inspect().stats() or {} + return HttpResponse(json.dumps(stats, indent=4), + mimetype="application/json") + + +@dog_stats_api.timed('status.service.celery.ping') +def celery_ping(_): + """ + A Simple view that checks if Celery can process a simple task + """ + start = time.time() + result = tasks.delayed_ping.apply_async(('ping', 0.1)) + task_id = result.id + + # Wait until we get the result + try: + value = result.get(timeout=4.0) + success = True + except TimeoutError: + value = None + success = False + + output = { + 'success': success, + 'task_id': task_id, + 'value': value, + 'time': time.time() - start, + } + + return HttpResponse(json.dumps(output, indent=4), + mimetype="application/json") diff --git a/lms/envs/common.py b/lms/envs/common.py index 64b07ef855..cc03915fb2 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -101,6 +101,9 @@ MITX_FEATURES = { # Turn on a page that lets staff enter Python code to be run in the # sandbox, for testing whether it's enabled properly. 'ENABLE_DEBUG_RUN_PYTHON': False, + + # Enable URL that shows information about the status of variuous services + 'ENABLE_SERVICE_STATUS': False, } # Used for A/B testing diff --git a/lms/envs/dev.py b/lms/envs/dev.py index 28c9a57a16..488110655e 100644 --- a/lms/envs/dev.py +++ b/lms/envs/dev.py @@ -22,7 +22,7 @@ MITX_FEATURES['FORCE_UNIVERSITY_DOMAIN'] = None # show all university courses i MITX_FEATURES['ENABLE_MANUAL_GIT_RELOAD'] = True MITX_FEATURES['ENABLE_PSYCHOMETRICS'] = False # real-time psychometrics (eg item response theory analysis in instructor dashboard) MITX_FEATURES['ENABLE_INSTRUCTOR_ANALYTICS'] = True - +MITX_FEATURES['ENABLE_SERVICE_STATUS'] = True WIKI_ENABLED = True diff --git a/lms/envs/test.py b/lms/envs/test.py index 9f3dea3980..003f7933be 100644 --- a/lms/envs/test.py +++ b/lms/envs/test.py @@ -18,6 +18,8 @@ MITX_FEATURES['DISABLE_START_DATES'] = True # Until we have discussion actually working in test mode, just turn it off MITX_FEATURES['ENABLE_DISCUSSION_SERVICE'] = False +MITX_FEATURES['ENABLE_SERVICE_STATUS'] = True + # Need wiki for courseware views to work. TODO (vshnayder): shouldn't need it. WIKI_ENABLED = True diff --git a/lms/urls.py b/lms/urls.py index 99b55fdb54..bf54cfc6c0 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -357,6 +357,11 @@ if settings.MITX_FEATURES.get('ENABLE_SQL_TRACKING_LOGS'): url(r'^event_logs/(?P.+)$', 'track.views.view_tracking_log'), ) +if settings.MITX_FEATURES.get('ENABLE_SERVICE_STATUS'): + urlpatterns += ( + url(r'^status/', include('service_status.urls')), + ) + # FoldIt views urlpatterns += ( # The path is hardcoded into their app...