From 06e3e4a8d4a9ec27237169c788653f02e70cbb3a Mon Sep 17 00:00:00 2001 From: Manali Latkar Date: Thu, 27 Feb 2014 15:11:31 +0530 Subject: [PATCH] adding an api to get count of .verified in rawdata --- docs/dbapi.rst | 35 ++++++++++++++ stacktach/dbapi.py | 33 +++++++++++++ stacktach/urls.py | 1 + tests/unit/test_dbapi.py | 100 ++++++++++++++++++++++++++++++++++----- 4 files changed, 156 insertions(+), 13 deletions(-) diff --git a/docs/dbapi.rst b/docs/dbapi.rst index e6d596f..23d1731 100644 --- a/docs/dbapi.rst +++ b/docs/dbapi.rst @@ -779,4 +779,39 @@ Returns a single instance exists matching provided id "id": 5300, "delete": null } + } + +db/count/verified/ +================== + +.. http:get:: http://example.com/count/verified/ + +Returns a count of .verified events stored in Stacktach's Rawdata table from +``audit_period_beginning`` to ``audit_period_ending`` + + **Query Parameters** + + * ``audit_period_beginning``: datetime (yyyy-mm-dd) + * ``audit_period_ending``: datetime (yyyy-mm-dd) + * ``service``: ``nova`` or ``glance``. default="nova" + + **Example request**: + + .. sourcecode:: http + + GET db/count/verified/ HTTP/1.1 + Host: example.com + Accept: application/json + + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Vary: Accept + Content-Type: application/json + + { + count: 10 } \ No newline at end of file diff --git a/stacktach/dbapi.py b/stacktach/dbapi.py index 1d0cc4d..4b9be0f 100644 --- a/stacktach/dbapi.py +++ b/stacktach/dbapi.py @@ -21,6 +21,7 @@ import decimal import functools import json +from datetime import datetime from django.db import transaction from django.db.models import FieldDoesNotExist @@ -418,3 +419,35 @@ def _convert_model_list(model_list, extra_values_func=None): converted.append(_convert_model(item, extra_values_func)) return converted + + +def _rawdata_factory(service): + if service == "nova": + rawdata = models.RawData.objects + elif service == "glance": + rawdata = models.GlanceRawData.objects + else: + raise BadRequestException(message="Invalid service") + return rawdata + + +@api_call +def get_verified_count(request): + try: + audit_period_beginning = datetime.strptime( + request.GET.get("audit_period_beginning"), "%Y-%m-%d") + audit_period_ending = datetime.strptime( + request.GET.get("audit_period_ending"), "%Y-%m-%d") + service = request.GET.get("service", "nova") + rawdata = _rawdata_factory(service) + filters = { + 'when__gte': dt.dt_to_decimal(audit_period_beginning), + 'when__lte': dt.dt_to_decimal(audit_period_ending), + 'event': "compute.instance.exists.verified" + } + return {'count': rawdata.filter(**filters).count()} + except KeyError and TypeError: + raise BadRequestException(message="Invalid/absent query parameter") + except ValueError: + raise BadRequestException(message="Invalid format for date (Correct " + "format should be %YYYY-%mm-%dd)") diff --git a/stacktach/urls.py b/stacktach/urls.py index e49c217..4cde55d 100644 --- a/stacktach/urls.py +++ b/stacktach/urls.py @@ -71,6 +71,7 @@ urlpatterns = patterns('', 'stacktach.dbapi.get_usage_exist_glance'), url(r'db/confirm/usage/exists/(?P[\w\-]+)/$', 'stacktach.dbapi.exists_send_status'), + url(r'db/count/verified', 'stacktach.dbapi.get_verified_count'), url(r'^(?P\d+)/$', 'stacktach.views.home', name='home'), url(r'^(?P\d+)/details/(?P\w+)/(?P\d+)/$', diff --git a/tests/unit/test_dbapi.py b/tests/unit/test_dbapi.py index 4b335a5..fab764f 100644 --- a/tests/unit/test_dbapi.py +++ b/tests/unit/test_dbapi.py @@ -19,6 +19,7 @@ # IN THE SOFTWARE. import datetime +from decimal import Decimal import json from django.db.models import FieldDoesNotExist @@ -124,8 +125,8 @@ class DBAPITestCase(StacktachBaseTestCase): fake_request = self.mox.CreateMockAnything() fake_request.GET = {'somebadfield_max': str(start_time)} fake_model = self.make_fake_model() - fake_model._meta.get_field_by_name('somebadfield')\ - .AndRaise(FieldDoesNotExist()) + fake_model._meta.get_field_by_name('somebadfield') \ + .AndRaise(FieldDoesNotExist()) self.mox.ReplayAll() self.assertRaises(dbapi.BadRequestException, dbapi._get_filter_args, @@ -307,7 +308,8 @@ class DBAPITestCase(StacktachBaseTestCase): fake_request.GET = filters self.mox.StubOutWithMock(dbapi, '_get_filter_args') dbapi._get_filter_args(fake_model, fake_request, - custom_filters=custom_filters).AndReturn(filters) + custom_filters=custom_filters).AndReturn( + filters) self.mox.StubOutWithMock(dbapi, '_check_has_field') dbapi._check_has_field(fake_model, 'id') result = self.mox.CreateMockAnything() @@ -558,7 +560,8 @@ class DBAPITestCase(StacktachBaseTestCase): exists1.send_status = 200 self.mox.VerifyAll() - def test_send_status_batch_accepts_post_for_nova_and_glance_when_version_is_1(self): + def test_send_status_batch_accepts_post_for_nova_and_glance_when_version_is_1( + self): fake_request = self.mox.CreateMockAnything() fake_request.method = 'POST' fake_request.GET = {'service': 'glance'} @@ -586,14 +589,16 @@ class DBAPITestCase(StacktachBaseTestCase): models.ImageExists.objects.select_for_update().AndReturn(results1) exists1A = self.mox.CreateMockAnything() exists1B = self.mox.CreateMockAnything() - results1.filter(message_id=MESSAGE_ID_2).AndReturn([exists1A, exists1B]) + results1.filter(message_id=MESSAGE_ID_2).AndReturn( + [exists1A, exists1B]) exists1A.save() exists1B.save() results2 = self.mox.CreateMockAnything() models.ImageExists.objects.select_for_update().AndReturn(results2) exists2A = self.mox.CreateMockAnything() exists2B = self.mox.CreateMockAnything() - results2.filter(message_id=MESSAGE_ID_1).AndReturn([exists2A, exists2B]) + results2.filter(message_id=MESSAGE_ID_1).AndReturn( + [exists2A, exists2B]) exists2A.save() exists2B.save() trans_obj.__exit__(None, None, None) @@ -604,7 +609,6 @@ class DBAPITestCase(StacktachBaseTestCase): self.mox.VerifyAll() - def test_send_status_batch_accepts_post_when_version_is_0(self): fake_request = self.mox.CreateMockAnything() fake_request.method = 'POST' @@ -759,7 +763,8 @@ class DBAPITestCase(StacktachBaseTestCase): launches = {'a': 1} self.mox.StubOutWithMock(dbapi, '_convert_model_list') dbapi._convert_model_list(mock_objects).AndReturn(launches) - dbapi.get_db_objects(models.InstanceUsage, fake_request, 'launched_at').AndReturn(mock_objects) + dbapi.get_db_objects(models.InstanceUsage, fake_request, + 'launched_at').AndReturn(mock_objects) self.mox.ReplayAll() resp = dbapi.list_usage_launches(fake_request) @@ -776,7 +781,8 @@ class DBAPITestCase(StacktachBaseTestCase): launches = {'a': 1} self.mox.StubOutWithMock(dbapi, '_convert_model_list') dbapi._convert_model_list(mock_objects).AndReturn(launches) - dbapi.get_db_objects(models.ImageUsage, fake_request, 'created_at').AndReturn(mock_objects) + dbapi.get_db_objects(models.ImageUsage, fake_request, + 'created_at').AndReturn(mock_objects) self.mox.ReplayAll() resp = dbapi.list_usage_images(fake_request) @@ -793,7 +799,8 @@ class DBAPITestCase(StacktachBaseTestCase): launches = {'a': 1} self.mox.StubOutWithMock(dbapi, '_convert_model_list') dbapi._convert_model_list(mock_objects).AndReturn(launches) - dbapi.get_db_objects(models.InstanceUsage, fake_request, 'launched_at').AndReturn(mock_objects) + dbapi.get_db_objects(models.InstanceUsage, fake_request, + 'launched_at').AndReturn(mock_objects) self.mox.ReplayAll() resp = dbapi.list_usage_launches(fake_request) @@ -880,7 +887,8 @@ class DBAPITestCase(StacktachBaseTestCase): deletes = {'a': 1} self.mox.StubOutWithMock(dbapi, '_convert_model_list') dbapi._convert_model_list(mock_objects).AndReturn(deletes) - dbapi.get_db_objects(models.InstanceDeletes, fake_request, 'launched_at').AndReturn(mock_objects) + dbapi.get_db_objects(models.InstanceDeletes, fake_request, + 'launched_at').AndReturn(mock_objects) self.mox.ReplayAll() resp = dbapi.list_usage_deletes(fake_request) @@ -897,7 +905,8 @@ class DBAPITestCase(StacktachBaseTestCase): deletes = {'a': 1} self.mox.StubOutWithMock(dbapi, '_convert_model_list') dbapi._convert_model_list(mock_objects).AndReturn(deletes) - dbapi.get_db_objects(models.InstanceDeletes, fake_request, 'launched_at').AndReturn(mock_objects) + dbapi.get_db_objects(models.InstanceDeletes, fake_request, + 'launched_at').AndReturn(mock_objects) self.mox.ReplayAll() resp = dbapi.list_usage_deletes(fake_request) @@ -914,10 +923,75 @@ class DBAPITestCase(StacktachBaseTestCase): deletes = {'a': 1} self.mox.StubOutWithMock(dbapi, '_convert_model_list') dbapi._convert_model_list(mock_objects).AndReturn(deletes) - dbapi.get_db_objects(models.ImageDeletes, fake_request, 'deleted_at').AndReturn(mock_objects) + dbapi.get_db_objects(models.ImageDeletes, fake_request, + 'deleted_at').AndReturn(mock_objects) self.mox.ReplayAll() resp = dbapi.list_usage_deletes_glance(fake_request) self.assertEqual(resp.status_code, 200) self.assertEqual(json.loads(resp.content), {'deletes': deletes}) self.mox.VerifyAll() + + def test_get_verified_count(self): + fake_request = self.mox.CreateMockAnything() + fake_request.method = 'GET' + fake_request.GET = {'audit_period_beginning': "2014-02-26", + 'audit_period_ending': "2014-02-27", + 'service': "nova"} + mock_query = self.mox.CreateMockAnything() + self.mox.StubOutWithMock(models.RawData.objects, "filter") + models.RawData.objects.filter(event='compute.instance.exists.verified', + when__gte=Decimal('1393372800'), + when__lte=Decimal('1393459200')).\ + AndReturn(mock_query) + mock_query.count().AndReturn(100) + self.mox.ReplayAll() + + response = dbapi.get_verified_count(fake_request) + self.assertEqual(response.status_code, 200) + self.assertEqual(json.loads(response.content), {'count': 100}) + self.mox.VerifyAll() + + def test_get_verified_count_wrong_date_format_returns_400(self): + fake_request = self.mox.CreateMockAnything() + fake_request.method = 'GET' + fake_request.GET = {'audit_period_beginning': "2014-020-26", + + 'service': "nova"} + + self.mox.ReplayAll() + + response = dbapi.get_verified_count(fake_request) + self.assertEqual(response.status_code, 400) + self.assertEqual(json.loads(response.content)['message'], + "Invalid format for date" + " (Correct format should be %YYYY-%mm-%dd)") + self.mox.VerifyAll() + + def test_get_verified_count_wrong_service_returns_400(self): + fake_request = self.mox.CreateMockAnything() + fake_request.method = 'GET' + fake_request.GET = {'audit_period_beginning': "2014-02-26", + "audit_period_ending": "2014-02-27", + 'service': "qonos"} + + self.mox.ReplayAll() + + response = dbapi.get_verified_count(fake_request) + self.assertEqual(response.status_code, 400) + self.assertEqual(json.loads(response.content)['message'], + "Invalid service") + self.mox.VerifyAll() + + def test_get_verified_count_invalid_query_parameter_returns_400(self): + fake_request = self.mox.CreateMockAnything() + fake_request.method = 'GET' + fake_request.GET = {'audit_period': "2014-02-26",} + + self.mox.ReplayAll() + + response = dbapi.get_verified_count(fake_request) + self.assertEqual(response.status_code, 400) + self.assertEqual(json.loads(response.content)['message'], + "Invalid/absent query parameter") + self.mox.VerifyAll() \ No newline at end of file