diff --git a/stacktach/db.py b/stacktach/db.py new file mode 100644 index 0000000..386ac40 --- /dev/null +++ b/stacktach/db.py @@ -0,0 +1,16 @@ +import models + +def create_lifecycle(**kwargs): + return models.Lifecycle(**kwargs) + +def find_lifecycles(**kwargs): + return models.Lifecycle.objects.select_related().filter(**kwargs) + +def create_timing(**kwargs): + return models.Timing(**kwargs) + +def find_timings(**kwargs): + return models.Timing.objects.select_related().filter(**kwargs) + +def save(obj): + obj.save() \ No newline at end of file diff --git a/stacktach/tests.py b/stacktach/tests.py index 1da4737..370a828 100644 --- a/stacktach/tests.py +++ b/stacktach/tests.py @@ -603,6 +603,7 @@ class ViewsUsageTestCase(unittest.TestCase): self.assertEqual(exists.deleted_at, deleted) self.assertEqual(exists.instance_type_id, '1') + class ViewsUsageWorkflowTestCase(unittest.TestCase): def setUp(self): self.deployment = Deployment(name='TestDeployment') diff --git a/stacktach/views.py b/stacktach/views.py index 4a96bff..f8eb847 100644 --- a/stacktach/views.py +++ b/stacktach/views.py @@ -5,6 +5,7 @@ from django import http from django.shortcuts import render_to_response from django import template +from stacktach import db as stackdb from stacktach import models from stacktach import datetime_to_decimal as dt @@ -13,6 +14,9 @@ import json import pprint +STACKDB = stackdb + + def _extract_states(payload): return { 'state' : payload.get('state', ""), @@ -129,16 +133,15 @@ def aggregate_lifecycle(raw): # While we hope only one lifecycle ever exists it's quite # likely we get multiple due to the workers and threads. lifecycle = None - lifecycles = models.Lifecycle.objects.select_related().\ - filter(instance=raw.instance) + lifecycles = STACKDB.find_lifecycles(instance=raw.instance) if len(lifecycles) > 0: lifecycle = lifecycles[0] if not lifecycle: - lifecycle = models.Lifecycle(instance=raw.instance) + lifecycle = STACKDB.create_lifecycle(instance=raw.instance) lifecycle.last_raw = raw lifecycle.last_state = raw.state lifecycle.last_task_state = raw.old_task - lifecycle.save() + STACKDB.save(lifecycle) event = raw.event parts = event.split('.') @@ -160,8 +163,7 @@ def aggregate_lifecycle(raw): # *shouldn't* happen). start = step == 'start' timing = None - timings = models.Timing.objects.select_related().\ - filter(name=name, lifecycle=lifecycle) + timings = STACKDB.find_timings(name=name, lifecycle=lifecycle) if not start: for t in timings: try: @@ -173,7 +175,7 @@ def aggregate_lifecycle(raw): pass if timing is None: - timing = models.Timing(name=name, lifecycle=lifecycle) + timing = STACKDB.create_timing(name=name, lifecycle=lifecycle) if start: timing.start_raw = raw @@ -197,7 +199,7 @@ def aggregate_lifecycle(raw): timing.diff = timing.end_when - timing.start_when # Looks like a valid pair ... update_kpi(lifecycle, timing, raw) - timing.save() + STACKDB.save(timing) INSTANCE_EVENT = { diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..d90f00b --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,6 @@ +import os +import sys + +print "!!!!!!! %s" %__package__ + +#sys.path = [os.path.abspath(os.path.dirname(__package__))] + sys.path \ No newline at end of file diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/test_stacktach.py b/tests/unit/test_stacktach.py new file mode 100644 index 0000000..850ea93 --- /dev/null +++ b/tests/unit/test_stacktach.py @@ -0,0 +1,126 @@ +import datetime +import os +import sys +import unittest + +import mox + +INSTANCE_ID_1 = 'testinstanceid1' +INSTANCE_ID_2 = 'testinstanceid2' + +os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' +sys.path = [os.path.abspath(os.path.dirname('stacktach'))] + sys.path + +from stacktach import views +import test_utils as utils + +class StacktachLifecycleTestCase(unittest.TestCase): + def setUp(self): + self.mox = mox.Mox() + views.STACKDB = self.mox.CreateMockAnything() + + def tearDown(self): + pass + + def test_aggregate_lifecycle_no_instance(self): + raw = self.mox.CreateMockAnything() + raw.instance = None + + views.aggregate_lifecycle(raw) + + self.mox.ReplayAll() + self.mox.VerifyAll() + + def test_aggregate_lifecycle_start(self): + event_name = 'compute.instance.create' + event = '%s.start' % event_name + when = datetime.datetime.utcnow() + raw = utils.create_raw(self.mox, when, event, state='building') + + views.STACKDB.find_lifecycles(instance=INSTANCE_ID_1).AndReturn([]) + lifecycle = self.mox.CreateMockAnything() + lifecycle.instance = INSTANCE_ID_1 + views.STACKDB.create_lifecycle(instance=INSTANCE_ID_1).AndReturn(lifecycle) + views.STACKDB.save(lifecycle) + + views.STACKDB.find_timings(name=event_name, lifecycle=lifecycle).AndReturn([]) + timing = utils.create_timing(self.mox, event_name, lifecycle) + views.STACKDB.create_timing(lifecycle=lifecycle, name=event_name).AndReturn(timing) + views.STACKDB.save(timing) + + self.mox.ReplayAll() + views.aggregate_lifecycle(raw) + self.assertEqual(lifecycle.last_raw, raw) + self.assertEqual(lifecycle.last_state, 'building') + self.assertEqual(lifecycle.last_task_state, '') + self.assertEqual(timing.name, event_name) + self.assertEqual(timing.lifecycle, lifecycle) + self.assertEqual(timing.start_raw, raw) + self.assertEqual(timing.start_when, when) + + self.mox.VerifyAll() + + def test_aggregate_lifecycle_end(self): + event_name = 'compute.instance.create' + start_event = '%s.end' % event_name + end_event = '%s.end' % event_name + start_when = datetime.datetime.utcnow() + end_when = datetime.datetime.utcnow() + start_raw = utils.create_raw(self.mox, start_when, start_event, + state='building') + end_raw = utils.create_raw(self.mox, end_when, end_event, + old_task='build') + + lifecycle = utils.create_lifecycle(self.mox, INSTANCE_ID_1, + 'active', '', start_raw) + views.STACKDB.find_lifecycles(instance=INSTANCE_ID_1).AndReturn([lifecycle]) + views.STACKDB.save(lifecycle) + + timing = utils.create_timing(self.mox, event_name, lifecycle, + start_raw=start_raw, + start_when=start_when) + views.STACKDB.find_timings(name=event_name, lifecycle=lifecycle).AndReturn([timing]) + + self.mox.StubOutWithMock(views, "update_kpi") + views.update_kpi(lifecycle, timing, end_raw) + views.STACKDB.save(timing) + + self.mox.ReplayAll() + views.aggregate_lifecycle(end_raw) + self.assertEqual(lifecycle.last_raw, end_raw) + self.assertEqual(lifecycle.last_state, 'active') + self.assertEqual(lifecycle.last_task_state, 'build') + self.assertEqual(timing.name, event_name) + self.assertEqual(timing.lifecycle, lifecycle) + self.assertEqual(timing.start_raw, start_raw) + self.assertEqual(timing.start_when, start_when) + self.assertEqual(timing.end_raw, end_raw) + self.assertEqual(timing.end_when, end_when) + self.assertEqual(timing.diff, end_when-start_when) + + self.mox.UnsetStubs() + self.mox.VerifyAll() + + + def test_aggregate_lifecycle_update(self): + event = 'compute.instance.update' + when = datetime.datetime.utcnow() + raw = utils.create_raw(self.mox, when, event, old_task='reboot') + + views.STACKDB.find_lifecycles(instance=INSTANCE_ID_1).AndReturn([]) + lifecycle = self.mox.CreateMockAnything() + lifecycle.instance = INSTANCE_ID_1 + views.STACKDB.create_lifecycle(instance=INSTANCE_ID_1).AndReturn(lifecycle) + views.STACKDB.save(lifecycle) + + self.mox.StubOutWithMock(views, "start_kpi_tracking") + views.start_kpi_tracking(lifecycle, raw) + + self.mox.ReplayAll() + views.aggregate_lifecycle(raw) + self.assertEqual(lifecycle.last_raw, raw) + self.assertEqual(lifecycle.last_state, 'active') + self.assertEqual(lifecycle.last_task_state, 'reboot') + + self.mox.UnsetStubs() + self.mox.VerifyAll() diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py new file mode 100644 index 0000000..82fa2e3 --- /dev/null +++ b/tests/unit/test_utils.py @@ -0,0 +1,44 @@ + +INSTANCE_ID_1 = 'testinstanceid1' +INSTANCE_ID_2 = 'testinstanceid2' + +MESSAGE_ID_1 = 'testmessageid1' +MESSAGE_ID_2 = 'testmessageid2' + +REQUEST_ID_1 = 'testrequestid1' +REQUEST_ID_2 = 'testrequestid2' +REQUEST_ID_3 = 'testrequestid3' + +def create_raw(mox, when, event, instance=INSTANCE_ID_1, + request_id=REQUEST_ID_1, state='active', old_task='', + host='compute', json=''): + raw = mox.CreateMockAnything() + raw.host = host + raw.instance = instance + raw.event = event + raw.when = when + raw.state = state + raw.old_task = old_task + raw.request_id = request_id, + raw.json = json + return raw + +def create_lifecycle(mox, instance, last_state, last_task_state, last_raw): + lifecycle = mox.CreateMockAnything() + lifecycle.instance = instance + lifecycle.last_state = last_state + lifecycle.last_task_state = last_task_state + lifecycle.last_raw = last_raw + return lifecycle + +def create_timing(mox, name, lifecycle, start_raw=None, start_when=None, + end_raw=None, end_when=None, diff=None): + timing = mox.CreateMockAnything() + timing.name = name + timing.lifecycle = lifecycle + timing.start_raw = start_raw + timing.start_when = start_when + timing.end_raw = end_raw + timing.end_when = end_when + timing.diff = diff + return timing \ No newline at end of file diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..630adc5 --- /dev/null +++ b/tox.ini @@ -0,0 +1,28 @@ +[tox] +envlist = py26,py27,pep8 + +[testenv] +setenv = VIRTUAL_ENV={envdir} +# NOSE_WITH_OPENSTACK=1 +# NOSE_OPENSTACK_COLOR=1 +# NOSE_OPENSTACK_RED=0.05 +# NOSE_OPENSTACK_YELLOW=0.025 +# NOSE_OPENSTACK_SHOW_ELAPSED=1 +# NOSE_OPENSTACK_STDOUT=1 +#deps = -r{toxinidir}/tools/pip-requires +# -r{toxinidir}/tools/test-requires +#commands = nosetests {posargs} + +[tox:jenkins] +downloadcache = ~/cache/pip + +[testenv:pep8] +deps = pep8==1.3.3 +commands = + pep8 --ignore=E125,E126,E711,E712 --repeat --show-source --exclude=.venv,.tox,dist,doc,openstack . + +[testenv:cover] +setenv = NOSE_WITH_COVERAGE=1 + +[testenv:venv] +commands = {posargs}