From 97666bcb6c4f865a424cd86d307fbafd99374855 Mon Sep 17 00:00:00 2001 From: Manali Latkar Date: Mon, 17 Feb 2014 18:36:24 +0530 Subject: [PATCH] Stacktach down scenario: added an api to repair exists status after a period of verifier inactivity added documentation --- docs/api.rst | 2 +- docs/dbapi.rst | 38 +++++++++- stacktach/dbapi.py | 22 ++++++ stacktach/models.py | 36 +++++++++ stacktach/urls.py | 3 +- tests/unit/test_dbapi.py | 51 +++++++++++++ tests/unit/test_models.py | 138 +++++++++++++++++++++++++++++++++++ tests/unit/test_stacktach.py | 3 +- 8 files changed, 288 insertions(+), 5 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 8b559dc..0644414 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -805,4 +805,4 @@ stacky/usage/exists ] ] - :query instance: desired instance UUID (optional) + :query instance: desired instance UUID (optional) \ No newline at end of file diff --git a/docs/dbapi.rst b/docs/dbapi.rst index 23d1731..9a4daef 100644 --- a/docs/dbapi.rst +++ b/docs/dbapi.rst @@ -814,4 +814,40 @@ Returns a count of .verified events stored in Stacktach's Rawdata table from { count: 10 - } \ No newline at end of file + } + +repair +====== + +.. http:post:: http://example.com/repair/ + + Changes the status of all the exists of message-ids sent with the request + from 'pending' to 'sent_unverified' so that the verifier does not end up + sending .verified for all those exists(since the .exists have already been + modified as .verified and sent to AH by Yagi). It sends back the message-ids + of exists which could not be updated in the json response. + + **Example request**: + + .. sourcecode::http + + POST /repair/ HTTP/1.1 + Host: example.com + Accept: application/json + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Vary: Accept + Content-Type: text/json + + { + u'exists_not_pending': [u'494ebfce-0219-4b62-b810-79039a279620'], + u'absent_exists': [u'7609f3b2-3694-4b6f-869e-2f13ae504cb2', + u'0c64032e-4a60-44c0-a99d-5a4f2e46afb0'] + } + + :query message_ids: list of message_ids of exists messages + :query service: ``nova`` or ``glance``. default="nova" \ No newline at end of file diff --git a/stacktach/dbapi.py b/stacktach/dbapi.py index 4b9be0f..6450007 100644 --- a/stacktach/dbapi.py +++ b/stacktach/dbapi.py @@ -36,6 +36,7 @@ from stacktach import datetime_to_decimal as dt from stacktach import models from stacktach import stacklog from stacktach import utils +from stacktach.models import InstanceExists, ImageExists DEFAULT_LIMIT = 50 HARD_LIMIT = 1000 @@ -451,3 +452,24 @@ def get_verified_count(request): except ValueError: raise BadRequestException(message="Invalid format for date (Correct " "format should be %YYYY-%mm-%dd)") + + +def exists_factory(service): + model = InstanceExists + if service == 'glance': + model = ImageExists + return model + + +def repair_stacktach_down(request): + post_dict = dict((request.POST._iterlists())) + message_ids = post_dict.get('message_ids') + service = post_dict.get('service', ['nova']) + absent_exists, exists_not_pending = \ + exists_factory(service[0]).mark_exists_as_sent_unverified(message_ids) + response_data = {'absent_exists': absent_exists, + 'exists_not_pending': exists_not_pending} + response = HttpResponse(json.dumps(response_data), + content_type="application/json") + return response + diff --git a/stacktach/models.py b/stacktach/models.py index 9d1e0fb..d9afe35 100644 --- a/stacktach/models.py +++ b/stacktach/models.py @@ -338,6 +338,24 @@ class InstanceExists(models.Model): def update_status(self, new_status): self.status = new_status + @staticmethod + def mark_exists_as_sent_unverified(message_ids): + absent_exists = [] + exists_not_pending = [] + for message_id in message_ids: + try: + exists = InstanceExists.objects.get(message_id=message_id) + if exists.status == InstanceExists.PENDING: + exists.status = InstanceExists.SENT_UNVERIFIED + exists.save() + else: + exists_not_pending.append(message_id) + except Exception: + absent_exists.append(message_id) + return absent_exists, exists_not_pending + + + class Timing(models.Model): """Each Timing record corresponds to a .start/.end event pair @@ -536,6 +554,24 @@ class ImageExists(models.Model): self.fail_reason = reason self.save() + @staticmethod + def mark_exists_as_sent_unverified(message_ids): + absent_exists = [] + exists_not_pending = [] + for message_id in message_ids: + exists_list = ImageExists.objects.filter(message_id=message_id) + if exists_list: + for exists in exists_list: + if exists.status == ImageExists.PENDING: + exists.status = ImageExists.SENT_UNVERIFIED + exists.save() + else: + exists_not_pending.append(message_id) + else : + absent_exists.append(message_id) + return absent_exists, exists_not_pending + + def get_model_fields(model): return model._meta.fields diff --git a/stacktach/urls.py b/stacktach/urls.py index 4cde55d..a965594 100644 --- a/stacktach/urls.py +++ b/stacktach/urls.py @@ -72,6 +72,7 @@ urlpatterns = patterns('', 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'repair/', 'stacktach.dbapi.repair_stacktach_down'), url(r'^(?P\d+)/$', 'stacktach.views.home', name='home'), url(r'^(?P\d+)/details/(?P\w+)/(?P\d+)/$', @@ -83,5 +84,5 @@ urlpatterns = patterns('', url(r'^(?P\d+)/latest_raw/$', 'stacktach.views.latest_raw', name='latest_raw'), url(r'^(?P\d+)/instance_status/$', - 'stacktach.views.instance_status', name='instance_status'), + 'stacktach.views.instance_status', name='instance_status') ) diff --git a/tests/unit/test_dbapi.py b/tests/unit/test_dbapi.py index fab764f..7739c9e 100644 --- a/tests/unit/test_dbapi.py +++ b/tests/unit/test_dbapi.py @@ -994,4 +994,55 @@ class DBAPITestCase(StacktachBaseTestCase): self.assertEqual(response.status_code, 400) self.assertEqual(json.loads(response.content)['message'], "Invalid/absent query parameter") + self.mox.VerifyAll() + +class StacktachRepairScenarioApi(StacktachBaseTestCase): + def setUp(self): + self.mox = mox.Mox() + + def tearDown(self): + self.mox.UnsetStubs() + + def test_change_nova_exists_status_for_all_exists(self): + request = self.mox.CreateMockAnything() + request.POST = self.mox.CreateMockAnything() + message_ids = ["04fd94b5-64dd-4559-83b7-981d9d4f7a5a", + "14fd94b5-64dd-4559-83b7-981d9d4f7a5a", + "24fd94b5-64dd-4559-83b7-981d9d4f7a5a"] + request.POST._iterlists().AndReturn([('service', 'nova'), + ('message_ids', message_ids)]) + self.mox.StubOutWithMock(models.InstanceExists, + 'mark_exists_as_sent_unverified') + models.InstanceExists.mark_exists_as_sent_unverified(message_ids).\ + AndReturn([[], []]) + self.mox.ReplayAll() + + response = dbapi.repair_stacktach_down(request) + self.assertEqual(response.status_code, 200) + response_data = json.loads(response.content) + self.assertEqual(response_data['exists_not_pending'], []) + self.assertEqual(response_data['absent_exists'], []) + + self.mox.VerifyAll() + + def test_change_glance_exists_status_for_all_exists(self): + request = self.mox.CreateMockAnything() + request.POST = self.mox.CreateMockAnything() + message_ids = ['04fd94b5-64dd-4559-83b7-981d9d4f7a5a', + '14fd94b5-64dd-4559-83b7-981d9d4f7a5a', + '24fd94b5-64dd-4559-83b7-981d9d4f7a5a'] + request.POST._iterlists().AndReturn([('service', ['glance']), + ('message_ids', message_ids)]) + self.mox.StubOutWithMock(models.ImageExists, + 'mark_exists_as_sent_unverified') + models.ImageExists.mark_exists_as_sent_unverified(message_ids).\ + AndReturn([[], []]) + self.mox.ReplayAll() + + response = dbapi.repair_stacktach_down(request) + self.assertEqual(response.status_code, 200) + response_data = json.loads(response.content) + self.assertEqual(response_data['exists_not_pending'], []) + self.assertEqual(response_data['absent_exists'], []) + self.mox.VerifyAll() \ No newline at end of file diff --git a/tests/unit/test_models.py b/tests/unit/test_models.py index ea32261..f2c7a03 100644 --- a/tests/unit/test_models.py +++ b/tests/unit/test_models.py @@ -112,6 +112,81 @@ class ImageExistsTestCase(unittest.TestCase): 'owner1-3': [exist4], 'owner2-2': [exist2]}) + def test_mark_exists_as_sent_unverified(self): + message_ids = ["0708cb0b-6169-4d7c-9f58-3cf3d5bf694b", + "9156b83e-f684-4ec3-8f94-7e41902f27aa"] + + exist1 = self.mox.CreateMockAnything() + exist1.status = "pending" + exist1.save() + exist2 = self.mox.CreateMockAnything() + exist2.status = "pending" + exist2.save() + exist3 = self.mox.CreateMockAnything() + exist3.status = "pending" + exist3.save() + self.mox.StubOutWithMock(ImageExists.objects, 'filter') + ImageExists.objects.filter(message_id=message_ids[0]).AndReturn( + [exist1, exist2]) + ImageExists.objects.filter(message_id=message_ids[1]).AndReturn( + [exist3]) + self.mox.ReplayAll() + + results = ImageExists.mark_exists_as_sent_unverified(message_ids) + + self.assertEqual(results, ([], [])) + + self.mox.VerifyAll() + + def test_mark_exists_as_sent_unverified_return_absent_exists(self): + message_ids = ["0708cb0b-6169-4d7c-9f58-3cf3d5bf694b", + "9156b83e-f684-4ec3-8f94-7e41902f27aa"] + + exist1 = self.mox.CreateMockAnything() + exist1.status = "pending" + exist1.save() + exist2 = self.mox.CreateMockAnything() + exist2.status = "pending" + exist2.save() + self.mox.StubOutWithMock(ImageExists.objects, 'filter') + ImageExists.objects.filter(message_id=message_ids[0]).AndReturn( + [exist1, exist2]) + ImageExists.objects.filter(message_id=message_ids[1]).AndReturn([]) + self.mox.ReplayAll() + + results = ImageExists.mark_exists_as_sent_unverified(message_ids) + + self.assertEqual(results, (['9156b83e-f684-4ec3-8f94-7e41902f27aa'], + [])) + + self.mox.VerifyAll() + + def test_mark_exists_as_sent_unverified_and_return_exist_not_pending(self): + message_ids = ["0708cb0b-6169-4d7c-9f58-3cf3d5bf694b", + "9156b83e-f684-4ec3-8f94-7e41902f27aa"] + + exist1 = self.mox.CreateMockAnything() + exist1.status = "pending" + exist1.save() + exist2 = self.mox.CreateMockAnything() + exist2.status = "verified" + exist3 = self.mox.CreateMockAnything() + exist3.status = "pending" + exist3.save() + self.mox.StubOutWithMock(ImageExists.objects, 'filter') + ImageExists.objects.filter(message_id=message_ids[0]).AndReturn( + [exist1, exist2]) + ImageExists.objects.filter(message_id=message_ids[1]).AndReturn( + [exist3]) + self.mox.ReplayAll() + + results = ImageExists.mark_exists_as_sent_unverified(message_ids) + + self.assertEqual(results, ([], + ["0708cb0b-6169-4d7c-9f58-3cf3d5bf694b"])) + + self.mox.VerifyAll() + class InstanceExistsTestCase(unittest.TestCase): def setUp(self): @@ -137,3 +212,66 @@ class InstanceExistsTestCase(unittest.TestCase): self.mox.VerifyAll() self.assertEqual(results, [1, 2]) + + def test_mark_exists_as_sent_unverified(self): + message_ids = ["0708cb0b-6169-4d7c-9f58-3cf3d5bf694b", + "9156b83e-f684-4ec3-8f94-7e41902f27aa"] + + exist1 = self.mox.CreateMockAnything() + exist1.status = "pending" + exist1.save() + exist2 = self.mox.CreateMockAnything() + exist2.status = "pending" + exist2.save() + self.mox.StubOutWithMock(InstanceExists.objects, 'get') + InstanceExists.objects.get(message_id=message_ids[0]).AndReturn(exist1) + InstanceExists.objects.get(message_id=message_ids[1]).AndReturn(exist2) + self.mox.ReplayAll() + + results = InstanceExists.mark_exists_as_sent_unverified(message_ids) + + self.assertEqual(results, ([], [])) + + self.mox.VerifyAll() + + def test_mark_exists_as_sent_unverified_return_absent_exists(self): + message_ids = ["0708cb0b-6169-4d7c-9f58-3cf3d5bf694b", + "9156b83e-f684-4ec3-8f94-7e41902f27aa"] + + exist1 = self.mox.CreateMockAnything() + exist1.status = "pending" + exist1.save() + self.mox.StubOutWithMock(InstanceExists.objects, 'get') + InstanceExists.objects.get(message_id=message_ids[0]).AndReturn(exist1) + InstanceExists.objects.get(message_id=message_ids[1]).AndRaise( + Exception) + self.mox.ReplayAll() + + results = InstanceExists.mark_exists_as_sent_unverified(message_ids) + + self.assertEqual(results, (['9156b83e-f684-4ec3-8f94-7e41902f27aa'], + [])) + + self.mox.VerifyAll() + + def test_mark_exists_as_sent_unverified_and_return_exist_not_pending(self): + message_ids = ["0708cb0b-6169-4d7c-9f58-3cf3d5bf694b", + "9156b83e-f684-4ec3-8f94-7e41902f27aa"] + + exist1 = self.mox.CreateMockAnything() + exist1.status = "pending" + exist1.save() + exist2 = self.mox.CreateMockAnything() + exist2.status = "verified" + self.mox.StubOutWithMock(InstanceExists.objects, 'get') + InstanceExists.objects.get(message_id=message_ids[0]).AndReturn(exist1) + InstanceExists.objects.get(message_id=message_ids[1]).AndReturn(exist2) + self.mox.ReplayAll() + + results = InstanceExists.mark_exists_as_sent_unverified(message_ids) + + self.assertEqual(results, ([], + ["9156b83e-f684-4ec3-8f94-7e41902f27aa"])) + + self.mox.VerifyAll() + diff --git a/tests/unit/test_stacktach.py b/tests/unit/test_stacktach.py index 4ca4311..a668f13 100644 --- a/tests/unit/test_stacktach.py +++ b/tests/unit/test_stacktach.py @@ -38,8 +38,7 @@ from utils import TENANT_ID_1 from utils import INSTANCE_TYPE_ID_1 from utils import DUMMY_TIME from utils import INSTANCE_TYPE_ID_2 -from utils import IMAGE_UUID_1 -from stacktach import stacklog +from stacktach import stacklog, models from stacktach import notification from stacktach import views from tests.unit import StacktachBaseTestCase