From f5a1cf50982e707a62e94b6004f1e11b15068f32 Mon Sep 17 00:00:00 2001 From: Andrew Melton Date: Mon, 5 Aug 2013 16:13:26 -0400 Subject: [PATCH 1/3] Limits/Offsets in Stacky API Most Stacky API calls now have limit/offset query arguments. The default limit is 50 and the hard limit is 1000. --- stacktach/stacky_server.py | 135 ++++++++++++++++++++++++------- tests/unit/test_stacky_server.py | 133 +++++++++++++++++++++++++++--- 2 files changed, 227 insertions(+), 41 deletions(-) diff --git a/stacktach/stacky_server.py b/stacktach/stacky_server.py index c5b0552..6178427 100644 --- a/stacktach/stacky_server.py +++ b/stacktach/stacky_server.py @@ -14,6 +14,60 @@ from django.core.exceptions import ObjectDoesNotExist, FieldError SECS_PER_HOUR = 60 * 60 SECS_PER_DAY = SECS_PER_HOUR * 24 +DEFAULT_LIMIT = 50 +HARD_LIMIT = 1000 + + +def _get_limit(request): + limit = request.GET.get('limit', DEFAULT_LIMIT) + if limit: + limit = int(limit) + if limit > HARD_LIMIT: + limit = HARD_LIMIT + return limit + + +def _get_query_range(request): + limit = _get_limit(request) + offset = request.GET.get('offset') + + start = None + if offset: + start = int(offset) + else: + offset = 0 + + end = int(offset) + int(limit) + return start, end + + +def model_search(request, model, filters, + related=False, order_by=None, excludes=None): + + query = model + + if related: + query = query.select_related() + + if filters: + query = query.filter(**filters) + else: + query = query.all() + + if excludes: + for exclude in excludes: + if isinstance(exclude, dict): + query = query.exclude(**exclude) + else: + query = query.exclude(exclude) + + if order_by: + query = query.order_by(order_by) + + start, end = _get_query_range(request) + query = query[start:end] + return query + def get_event_names(service='nova'): return _model_factory(service).values('event').distinct() @@ -37,8 +91,10 @@ def get_deployments(): return models.Deployment.objects.all().order_by('name') -def get_timings_for_uuid(uuid): - lifecycles = models.Lifecycle.objects.filter(instance=uuid) +def get_timings_for_uuid(request, uuid): + model = models.Lifecycle.objects + filters = {'instance': uuid} + lifecycles = model_search(request, model, filters) results = [["?", "Event", "Time (secs)"]] for lc in lifecycles: @@ -118,7 +174,8 @@ def do_uuid(request, service='nova'): if service == 'glance': param = {'uuid': uuid} - related = model.select_related().filter(**param).order_by('when') + related = model_search(request, model, param, + related=True, order_by='when') for event in related: when = dt.dt_from_decimal(event.when) routing_key_status = routing_key_type(event.routing_key) @@ -131,24 +188,32 @@ def do_timings_uuid(request): if not utils.is_uuid_like(uuid): msg = "%s is not uuid-like" % uuid return error_response(400, 'Bad Request', msg) - results = get_timings_for_uuid(uuid) + results = get_timings_for_uuid(request, uuid) return rsp(json.dumps(results)) def do_timings(request): name = request.GET['name'] - results = [[name, "Time"]] - timings_query = models.Timing.objects.select_related()\ - .filter(name=name)\ - .exclude(Q(start_raw=None) | Q(end_raw=None)) + model = models.Timing.objects + + filters = { + 'name': name + } + if request.GET.get('end_when_min') is not None: min_when = decimal.Decimal(request.GET['end_when_min']) - timings_query = timings_query.filter(end_when__gte=min_when) + filters['end_when__gte'] = min_when + if request.GET.get('end_when_max') is not None: max_when = decimal.Decimal(request.GET['end_when_max']) - timings_query = timings_query.filter(end_when__lte=max_when) - timings = timings_query.order_by('diff') + filters['end_when__lte'] = max_when + excludes = [Q(start_raw=None) | Q(end_raw=None), ] + timings = model_search(request, model, filters, + excludes=excludes, related=True, + order_by='diff') + + results = [[name, "Time"]] for t in timings: results.append([t.lifecycle.instance, sec_to_time(t.diff)]) return rsp(json.dumps(results)) @@ -165,9 +230,14 @@ def do_summary(request): results = [["Event", "N", "Min", "Max", "Avg"]] for name in interesting: - timings = models.Timing.objects.filter(name=name) \ - .exclude(Q(start_raw=None) | Q(end_raw=None)) \ - .exclude(diff__lt=0) + model = models.Timing.objects + filters = {'name': name} + excludes = [ + Q(start_raw=None) | Q(end_raw=None), + {'diff__lt': 0} + ] + timings = model_search(request, model, filters, + excludes=excludes) if not timings: continue @@ -194,8 +264,9 @@ def do_request(request): msg = "%s is not request-id-like" % request_id return error_response(400, 'Bad Request', msg) - events = models.RawData.objects.filter(request_id=request_id) \ - .order_by('when') + model = models.RawData.objects + filters = {'request_id': request_id} + events = model_search(request, model, filters, order_by='when') results = [["#", "?", "When", "Deployment", "Event", "Host", "State", "State'", "Task'"]] for e in events: @@ -382,10 +453,11 @@ def do_list_usage_launches(request): return error_response(400, 'Bad Request', msg) filter_args['instance'] = uuid + model = models.InstanceUsage.objects if len(filter_args) > 0: - launches = models.InstanceUsage.objects.filter(**filter_args) + launches = model_search(request, model, filter_args) else: - launches = models.InstanceUsage.objects.all() + launches = model_search(request, model, None) results = [["UUID", "Launched At", "Instance Type Id"]] @@ -408,10 +480,11 @@ def do_list_usage_deletes(request): return error_response(400, 'Bad Request', msg) filter_args['instance'] = uuid + model = models.InstanceDeletes.objects if len(filter_args) > 0: - deletes = models.InstanceDeletes.objects.filter(**filter_args) + deletes = model_search(request, model, filter_args) else: - deletes = models.InstanceDeletes.objects.all() + deletes = model_search(request, model, None) results = [["UUID", "Launched At", "Deleted At"]] @@ -437,10 +510,11 @@ def do_list_usage_exists(request): return error_response(400, 'Bad Request', msg) filter_args['instance'] = uuid + model = models.InstanceExists.objects if len(filter_args) > 0: - exists = models.InstanceExists.objects.filter(**filter_args) + exists = model_search(request, model, filter_args) else: - exists = models.InstanceExists.objects.all() + exists = model_search(request, model, None) results = [["UUID", "Launched At", "Deleted At", "Instance Type Id", "Message ID", "Status"]] @@ -466,8 +540,12 @@ def do_jsonreports(request): now = dt.dt_to_decimal(now) _from = request.GET.get('created_from', yesterday) _to = request.GET.get('created_to', now) - reports = models.JsonReport.objects.filter(created__gte=_from, - created__lte=_to) + model = models.JsonReport.objects + filters = { + 'created__gte': _from, + 'created__lte': _to + } + reports = model_search(request, model, filters) results = [['Id', 'Start', 'End', 'Created', 'Name', 'Version']] for report in reports: results.append([report.id, @@ -486,19 +564,14 @@ def do_jsonreport(request, report_id): def search(request, service): - DEFAULT = 1000 field = request.GET.get('field') value = request.GET.get('value') - limit = request.GET.get('limit', DEFAULT) - limit = int(limit) model = _model_factory(service) filter_para = {field: value} results = [] try: - events = model.filter(**filter_para) - event_len = len(events) - if event_len > limit: - events = events[0:limit] + + events = model_search(request, model, filter_para) for event in events: when = dt.dt_from_decimal(event.when) routing_key_status = routing_key_type(event.routing_key) diff --git a/tests/unit/test_stacky_server.py b/tests/unit/test_stacky_server.py index 512ec98..85d5c5f 100644 --- a/tests/unit/test_stacky_server.py +++ b/tests/unit/test_stacky_server.py @@ -131,10 +131,13 @@ class StackyServerTestCase(unittest.TestCase): self.mox.VerifyAll() def test_get_timings_for_uuid_start_only(self): + fake_request = self.mox.CreateMockAnything() + fake_request.GET = {} lc_result = self.mox.CreateMockAnything() lifecycle = self.mox.CreateMockAnything() models.Lifecycle.objects.filter(instance=INSTANCE_ID_1)\ .AndReturn(lc_result) + lc_result[None:50].AndReturn(lc_result) lc_result.__iter__().AndReturn([lifecycle].__iter__()) t_result = self.mox.CreateMockAnything() timing = self.mox.CreateMockAnything() @@ -146,7 +149,8 @@ class StackyServerTestCase(unittest.TestCase): timing.diff = None self.mox.ReplayAll() - event_names = stacky_server.get_timings_for_uuid(INSTANCE_ID_1) + event_names = stacky_server.get_timings_for_uuid(fake_request, + INSTANCE_ID_1) self.assertEqual(len(event_names), 2) self.assertEqual(event_names[0], ['?', 'Event', 'Time (secs)']) @@ -154,10 +158,13 @@ class StackyServerTestCase(unittest.TestCase): self.mox.VerifyAll() def test_get_timings_for_uuid_end_only(self): + fake_request = self.mox.CreateMockAnything() + fake_request.GET = {} lc_result = self.mox.CreateMockAnything() lifecycle = self.mox.CreateMockAnything() models.Lifecycle.objects.filter(instance=INSTANCE_ID_1) \ .AndReturn(lc_result) + lc_result[None:50].AndReturn(lc_result) lc_result.__iter__().AndReturn([lifecycle].__iter__()) t_result = self.mox.CreateMockAnything() timing = self.mox.CreateMockAnything() @@ -169,7 +176,8 @@ class StackyServerTestCase(unittest.TestCase): timing.diff = None self.mox.ReplayAll() - event_names = stacky_server.get_timings_for_uuid(INSTANCE_ID_1) + event_names = stacky_server.get_timings_for_uuid(fake_request, + INSTANCE_ID_1) self.assertEqual(len(event_names), 2) self.assertEqual(event_names[0], ['?', 'Event', 'Time (secs)']) @@ -177,10 +185,13 @@ class StackyServerTestCase(unittest.TestCase): self.mox.VerifyAll() def test_get_timings_for_uuid(self): + fake_request = self.mox.CreateMockAnything() + fake_request.GET = {} lc_result = self.mox.CreateMockAnything() lifecycle = self.mox.CreateMockAnything() models.Lifecycle.objects.filter(instance=INSTANCE_ID_1) \ .AndReturn(lc_result) + lc_result[None:50].AndReturn(lc_result) lc_result.__iter__().AndReturn([lifecycle].__iter__()) t_result = self.mox.CreateMockAnything() timing = self.mox.CreateMockAnything() @@ -191,7 +202,8 @@ class StackyServerTestCase(unittest.TestCase): timing.end_raw = self.mox.CreateMockAnything() timing.diff = 20 self.mox.ReplayAll() - event_names = stacky_server.get_timings_for_uuid(INSTANCE_ID_1) + event_names = stacky_server.get_timings_for_uuid(fake_request, + INSTANCE_ID_1) self.assertEqual(len(event_names), 2) self.assertEqual(event_names[0], ['?', 'Event', 'Time (secs)']) @@ -272,6 +284,7 @@ class StackyServerTestCase(unittest.TestCase): result.filter(instance=INSTANCE_ID_1).AndReturn(result) result.order_by('when').AndReturn(result) raw = self._create_raw() + result[None:50].AndReturn(result) result.__iter__().AndReturn([raw].__iter__()) raw.search_results([], mox.IgnoreArg(), ' ').AndReturn(search_result) self.mox.ReplayAll() @@ -302,6 +315,7 @@ class StackyServerTestCase(unittest.TestCase): result.filter(uuid=INSTANCE_ID_1).AndReturn(result) result.order_by('when').AndReturn(result) raw = self._create_raw() + result[None:50].AndReturn(result) result.__iter__().AndReturn([raw].__iter__()) raw.search_results([], mox.IgnoreArg(), ' ').AndReturn(search_result) self.mox.ReplayAll() @@ -366,6 +380,7 @@ class StackyServerTestCase(unittest.TestCase): timing2.lifecycle = self.mox.CreateMockAnything() timing2.lifecycle.instance = INSTANCE_ID_2 timing2.diff = 20 + results[None:50].AndReturn(results) results.__iter__().AndReturn([timing1, timing2].__iter__()) self.mox.ReplayAll() @@ -385,9 +400,9 @@ class StackyServerTestCase(unittest.TestCase): fake_request.GET = {'name': 'test.event', 'end_when_min': '1.1'} results = self.mox.CreateMockAnything() models.Timing.objects.select_related().AndReturn(results) - results.filter(name='test.event').AndReturn(results) + results.filter(name='test.event', + end_when__gte=decimal.Decimal('1.1')).AndReturn(results) results.exclude(mox.IgnoreArg()).AndReturn(results) - results.filter(end_when__gte=decimal.Decimal('1.1')).AndReturn(results) results.order_by('diff').AndReturn(results) timing1 = self.mox.CreateMockAnything() timing1.lifecycle = self.mox.CreateMockAnything() @@ -397,6 +412,7 @@ class StackyServerTestCase(unittest.TestCase): timing2.lifecycle = self.mox.CreateMockAnything() timing2.lifecycle.instance = INSTANCE_ID_2 timing2.diff = 20 + results[None:50].AndReturn(results) results.__iter__().AndReturn([timing1, timing2].__iter__()) self.mox.ReplayAll() @@ -416,9 +432,9 @@ class StackyServerTestCase(unittest.TestCase): fake_request.GET = {'name': 'test.event', 'end_when_max': '1.1'} results = self.mox.CreateMockAnything() models.Timing.objects.select_related().AndReturn(results) - results.filter(name='test.event').AndReturn(results) + results.filter(name='test.event', + end_when__lte=decimal.Decimal('1.1')).AndReturn(results) results.exclude(mox.IgnoreArg()).AndReturn(results) - results.filter(end_when__lte=decimal.Decimal('1.1')).AndReturn(results) results.order_by('diff').AndReturn(results) timing1 = self.mox.CreateMockAnything() timing1.lifecycle = self.mox.CreateMockAnything() @@ -428,6 +444,7 @@ class StackyServerTestCase(unittest.TestCase): timing2.lifecycle = self.mox.CreateMockAnything() timing2.lifecycle.instance = INSTANCE_ID_2 timing2.diff = 20 + results[None:50].AndReturn(results) results.__iter__().AndReturn([timing1, timing2].__iter__()) self.mox.ReplayAll() @@ -449,10 +466,10 @@ class StackyServerTestCase(unittest.TestCase): 'end_when_max': '2.1'} results = self.mox.CreateMockAnything() models.Timing.objects.select_related().AndReturn(results) - results.filter(name='test.event').AndReturn(results) + results.filter(name='test.event', + end_when__gte=decimal.Decimal('1.1'), + end_when__lte=decimal.Decimal('2.1')).AndReturn(results) results.exclude(mox.IgnoreArg()).AndReturn(results) - results.filter(end_when__gte=decimal.Decimal('1.1')).AndReturn(results) - results.filter(end_when__lte=decimal.Decimal('2.1')).AndReturn(results) results.order_by('diff').AndReturn(results) timing1 = self.mox.CreateMockAnything() timing1.lifecycle = self.mox.CreateMockAnything() @@ -462,6 +479,7 @@ class StackyServerTestCase(unittest.TestCase): timing2.lifecycle = self.mox.CreateMockAnything() timing2.lifecycle.instance = INSTANCE_ID_2 timing2.diff = 20 + results[None:50].AndReturn(results) results.__iter__().AndReturn([timing1, timing2].__iter__()) self.mox.ReplayAll() @@ -494,6 +512,7 @@ class StackyServerTestCase(unittest.TestCase): timing2.lifecycle = self.mox.CreateMockAnything() timing2.lifecycle.instance = INSTANCE_ID_2 timing2.diff = 20 + results[None:50].AndReturn(results) results.__len__().AndReturn(2) results.__iter__().AndReturn([timing1, timing2].__iter__()) self.mox.ReplayAll() @@ -515,6 +534,7 @@ class StackyServerTestCase(unittest.TestCase): results = self.mox.CreateMockAnything() models.RawData.objects.filter(request_id=REQUEST_ID_1).AndReturn(results) results.order_by('when').AndReturn(results) + results[None:50].AndReturn(results) results.__iter__().AndReturn([raw].__iter__()) self.mox.ReplayAll() @@ -906,6 +926,7 @@ class StackyServerTestCase(unittest.TestCase): usage.instance = INSTANCE_ID_1 usage.launched_at = utils.decimal_utc() usage.instance_type_id = 1 + results[None:50].AndReturn(results) results.__iter__().AndReturn([usage].__iter__()) self.mox.ReplayAll() @@ -932,6 +953,7 @@ class StackyServerTestCase(unittest.TestCase): usage.instance = INSTANCE_ID_1 usage.launched_at = utils.decimal_utc() usage.instance_type_id = 1 + results[None:50].AndReturn(results) results.__iter__().AndReturn([usage].__iter__()) self.mox.ReplayAll() @@ -972,6 +994,7 @@ class StackyServerTestCase(unittest.TestCase): usage.instance = INSTANCE_ID_1 usage.launched_at = utils.decimal_utc() usage.deleted_at = usage.launched_at + 10 + results[None:50].AndReturn(results) results.__iter__().AndReturn([usage].__iter__()) self.mox.ReplayAll() @@ -998,6 +1021,7 @@ class StackyServerTestCase(unittest.TestCase): usage.instance = INSTANCE_ID_1 usage.launched_at = utils.decimal_utc() usage.deleted_at = usage.launched_at + 10 + results[None:50].AndReturn(results) results.__iter__().AndReturn([usage].__iter__()) self.mox.ReplayAll() @@ -1041,6 +1065,7 @@ class StackyServerTestCase(unittest.TestCase): usage.instance_type_id = 1 usage.message_id = 'someid' usage.status = 'pending' + results[None:50].AndReturn(results) results.__iter__().AndReturn([usage].__iter__()) self.mox.ReplayAll() @@ -1071,6 +1096,7 @@ class StackyServerTestCase(unittest.TestCase): usage.instance_type_id = 1 usage.message_id = 'someid' usage.status = 'pending' + results[None:50].AndReturn(results) results.__iter__().AndReturn([usage].__iter__()) self.mox.ReplayAll() @@ -1181,4 +1207,91 @@ class StackyServerTestCase(unittest.TestCase): json_resp = json.loads(resp.content) self.assertEqual(len(json_resp), 3) self._assert_on_search_nova(json_resp, raw1) + self.mox.VerifyAll() + + def test_model_search_default_limit(self): + fake_request = self.mox.CreateMockAnything() + fake_request.GET = {} + fake_model = self.mox.CreateMockAnything() + filters = {'field': 'value'} + results = self.mox.CreateMockAnything() + fake_model.filter(**filters).AndReturn(results) + results[None:50].AndReturn(results) + self.mox.ReplayAll() + actual_results = stacky_server.model_search(fake_request, fake_model, + filters) + self.assertEqual(actual_results, results) + self.mox.VerifyAll() + + def test_model_search_default_limit_with_offset(self): + fake_request = self.mox.CreateMockAnything() + fake_request.GET = {'offset': '1'} + fake_model = self.mox.CreateMockAnything() + filters = {'field': 'value'} + results = self.mox.CreateMockAnything() + fake_model.filter(**filters).AndReturn(results) + results[1:51].AndReturn(results) + self.mox.ReplayAll() + actual_results = stacky_server.model_search(fake_request, fake_model, + filters) + self.assertEqual(actual_results, results) + self.mox.VerifyAll() + + def test_model_search_default_with_limit(self): + fake_request = self.mox.CreateMockAnything() + fake_request.GET = {'limit': '1'} + fake_model = self.mox.CreateMockAnything() + filters = {'field': 'value'} + results = self.mox.CreateMockAnything() + fake_model.filter(**filters).AndReturn(results) + results[None:1].AndReturn(results) + self.mox.ReplayAll() + actual_results = stacky_server.model_search(fake_request, fake_model, + filters) + self.assertEqual(actual_results, results) + self.mox.VerifyAll() + + def test_model_search_default_with_limit_and_offset(self): + fake_request = self.mox.CreateMockAnything() + fake_request.GET = {'limit': '5', + 'offset': '10'} + fake_model = self.mox.CreateMockAnything() + filters = {'field': 'value'} + results = self.mox.CreateMockAnything() + fake_model.filter(**filters).AndReturn(results) + results[10:15].AndReturn(results) + self.mox.ReplayAll() + actual_results = stacky_server.model_search(fake_request, fake_model, + filters) + self.assertEqual(actual_results, results) + self.mox.VerifyAll() + + def test_model_search_related(self): + fake_request = self.mox.CreateMockAnything() + fake_request.GET = {} + fake_model = self.mox.CreateMockAnything() + filters = {'field': 'value'} + results = self.mox.CreateMockAnything() + fake_model.select_related().AndReturn(results) + results.filter(**filters).AndReturn(results) + results[None:50].AndReturn(results) + self.mox.ReplayAll() + actual_results = stacky_server.model_search(fake_request, fake_model, + filters, related=True) + self.assertEqual(actual_results, results) + self.mox.VerifyAll() + + def test_model_order_by(self): + fake_request = self.mox.CreateMockAnything() + fake_request.GET = {} + fake_model = self.mox.CreateMockAnything() + filters = {'field': 'value'} + results = self.mox.CreateMockAnything() + fake_model.filter(**filters).AndReturn(results) + results.order_by('when').AndReturn(results) + results[None:50].AndReturn(results) + self.mox.ReplayAll() + actual_results = stacky_server.model_search(fake_request, fake_model, + filters, order_by='when') + self.assertEqual(actual_results, results) self.mox.VerifyAll() \ No newline at end of file From d9558279c1be865d40110c8c6423e576558bfdcb Mon Sep 17 00:00:00 2001 From: Andrew Melton Date: Mon, 5 Aug 2013 17:14:03 -0400 Subject: [PATCH 2/3] Filter on when field for events in StackyAPI --- stacktach/stacky_server.py | 31 ++++++-- tests/unit/test_stacky_server.py | 129 +++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 8 deletions(-) diff --git a/stacktach/stacky_server.py b/stacktach/stacky_server.py index 6178427..fd7dd78 100644 --- a/stacktach/stacky_server.py +++ b/stacktach/stacky_server.py @@ -69,6 +69,16 @@ def model_search(request, model, filters, return query +def _add_when_filters(request, filters): + when_max = request.GET.get('when_max') + if when_max: + filters['when__lte'] = decimal.Decimal(when_max) + + when_min = request.GET.get('when_min') + if when_min: + filters['when__gte'] = decimal.Decimal(when_min) + + def get_event_names(service='nova'): return _model_factory(service).values('event').distinct() @@ -168,13 +178,16 @@ def do_uuid(request, service='nova'): return error_response(400, 'Bad Request', msg) model = _model_factory(service) result = [] - param = {} - if service == 'nova' or service == 'generic': - param = {'instance': uuid} - if service == 'glance': - param = {'uuid': uuid} + filters = {} - related = model_search(request, model, param, + if service == 'nova' or service == 'generic': + filters = {'instance': uuid} + if service == 'glance': + filters = {'uuid': uuid} + + _add_when_filters(request, filters) + + related = model_search(request, model, filters, related=True, order_by='when') for event in related: when = dt.dt_from_decimal(event.when) @@ -266,6 +279,7 @@ def do_request(request): model = models.RawData.objects filters = {'request_id': request_id} + _add_when_filters(request, filters) events = model_search(request, model, filters, order_by='when') results = [["#", "?", "When", "Deployment", "Event", "Host", "State", "State'", "Task'"]] @@ -567,11 +581,12 @@ def search(request, service): field = request.GET.get('field') value = request.GET.get('value') model = _model_factory(service) - filter_para = {field: value} + filters = {field: value} + _add_when_filters(request, filters) results = [] try: - events = model_search(request, model, filter_para) + events = model_search(request, model, filters) for event in events: when = dt.dt_from_decimal(event.when) routing_key_status = routing_key_type(event.routing_key) diff --git a/tests/unit/test_stacky_server.py b/tests/unit/test_stacky_server.py index 85d5c5f..27e1bc9 100644 --- a/tests/unit/test_stacky_server.py +++ b/tests/unit/test_stacky_server.py @@ -303,6 +303,41 @@ class StackyServerTestCase(unittest.TestCase): self.assertEqual(json_resp[1], body) self.mox.VerifyAll() + def test_do_uuid_when_filters(self): + search_result = [["#", "?", "When", "Deployment", "Event", "Host", + "State", "State'", "Task'"], [1, " ", + "2013-07-17 10:16:10.717219", "deployment", + "test.start", "example.com", "active", None, None]] + fake_request = self.mox.CreateMockAnything() + fake_request.GET = {'uuid': INSTANCE_ID_1, + 'when_min': '1.1', + 'when_max': '2.1'} + result = self.mox.CreateMockAnything() + models.RawData.objects.select_related().AndReturn(result) + result.filter(instance=INSTANCE_ID_1, + when__gte=decimal.Decimal('1.1'), + when__lte=decimal.Decimal('2.1')).AndReturn(result) + result.order_by('when').AndReturn(result) + raw = self._create_raw() + result[None:50].AndReturn(result) + result.__iter__().AndReturn([raw].__iter__()) + raw.search_results([], mox.IgnoreArg(), ' ').AndReturn(search_result) + self.mox.ReplayAll() + + resp = stacky_server.do_uuid(fake_request) + + self.assertEqual(resp.status_code, 200) + json_resp = json.loads(resp.content) + self.assertEqual(len(json_resp), 2) + header = ["#", "?", "When", "Deployment", "Event", "Host", + "State", "State'", "Task'"] + self.assertEqual(json_resp[0], header) + datetime = dt.dt_from_decimal(raw.when) + body = [1, " ", str(datetime), "deployment", "test.start", + "example.com", "active", None, None] + self.assertEqual(json_resp[1], body) + self.mox.VerifyAll() + def test_do_uuid_for_glance(self): search_result = [["#", "?", "When", "Deployment", "Event", "Host", "Status"], [1, " ", @@ -334,6 +369,41 @@ class StackyServerTestCase(unittest.TestCase): self.assertEqual(json_resp[1], body) self.mox.VerifyAll() + def test_do_uuid_for_glance_when_filters(self): + search_result = [["#", "?", "When", "Deployment", "Event", "Host", + "Status"], [1, " ", + "2013-07-17 10:16:10.717219", "deployment", + "test.start", "example.com", "state"]] + fake_request = self.mox.CreateMockAnything() + fake_request.GET = {'uuid': INSTANCE_ID_1, + 'when_min': '1.1', + 'when_max': '2.1'} + result = self.mox.CreateMockAnything() + models.GlanceRawData.objects.select_related().AndReturn(result) + result.filter(uuid=INSTANCE_ID_1, + when__gte=decimal.Decimal('1.1'), + when__lte=decimal.Decimal('2.1')).AndReturn(result) + result.order_by('when').AndReturn(result) + raw = self._create_raw() + result[None:50].AndReturn(result) + result.__iter__().AndReturn([raw].__iter__()) + raw.search_results([], mox.IgnoreArg(), ' ').AndReturn(search_result) + self.mox.ReplayAll() + + resp = stacky_server.do_uuid(fake_request,'glance') + + self.assertEqual(resp.status_code, 200) + json_resp = json.loads(resp.content) + self.assertEqual(len(json_resp), 2) + header = ["#", "?", "When", "Deployment", "Event", "Host", + "Status"] + self.assertEqual(json_resp[0], header) + datetime = dt.dt_from_decimal(raw.when) + body = [1, " ", str(datetime), "deployment", "test.start", + "example.com", "state"] + self.assertEqual(json_resp[1], body) + self.mox.VerifyAll() + def test_do_uuid_bad_uuid(self): fake_request = self.mox.CreateMockAnything() fake_request.GET = {'uuid': "obviouslybaduuid"} @@ -557,6 +627,42 @@ class StackyServerTestCase(unittest.TestCase): self.assertEqual(json_resp[1][8], None) self.mox.VerifyAll() + def test_do_request_when_filters(self): + fake_request = self.mox.CreateMockAnything() + fake_request.GET = {'request_id': REQUEST_ID_1, + 'when_min': '1.1', + 'when_max': '2.1'} + raw = self._create_raw() + results = self.mox.CreateMockAnything() + when_min = decimal.Decimal('1.1') + when_max = decimal.Decimal('2.1') + models.RawData.objects.filter(request_id=REQUEST_ID_1, + when__gte=when_min, + when__lte=when_max).AndReturn(results) + results.order_by('when').AndReturn(results) + results[None:50].AndReturn(results) + results.__iter__().AndReturn([raw].__iter__()) + self.mox.ReplayAll() + + resp = stacky_server.do_request(fake_request) + + self.assertEqual(resp.status_code, 200) + json_resp = json.loads(resp.content) + self.assertEqual(len(json_resp), 2) + self.assertEqual(json_resp[0], ["#", "?", "When", "Deployment", + "Event", "Host", "State", "State'", + "Task'"]) + self.assertEqual(json_resp[1][0], 1) + self.assertEqual(json_resp[1][1], u' ') + self.assertEqual(json_resp[1][2], str(dt.dt_from_decimal(raw.when))) + self.assertEqual(json_resp[1][3], u'deployment') + self.assertEqual(json_resp[1][4], u'test.start') + self.assertEqual(json_resp[1][5], u'example.com') + self.assertEqual(json_resp[1][6], u'active') + self.assertEqual(json_resp[1][7], None) + self.assertEqual(json_resp[1][8], None) + self.mox.VerifyAll() + def test_do_request_bad_request_id(self): fake_request = self.mox.CreateMockAnything() fake_request.GET = {'request_id': "obviouslybaduuid"} @@ -1177,6 +1283,29 @@ class StackyServerTestCase(unittest.TestCase): self._assert_on_search_nova(json_resp, raw) self.mox.VerifyAll() + def test_search_by_field_for_nova_when_filters(self): + search_result = [["#", "?", "When", "Deployment", "Event", "Host", + "State", "State'", "Task'"], [1, " ", + "2013-07-17 10:16:10.717219", "deployment", + "test.start", "example.com", "active", None, None]] + fake_request = self.mox.CreateMockAnything() + fake_request.GET = {'field': 'tenant', 'value': 'tenant', + 'when_min': '1.1', + 'when_max': '2.1'} + raw = self._create_raw() + models.RawData.objects.filter(tenant='tenant', + when__gte=decimal.Decimal('1.1'), + when__lte=decimal.Decimal('2.1')).AndReturn([raw]) + raw.search_results([], mox.IgnoreArg(), ' ').AndReturn(search_result) + self.mox.ReplayAll() + + resp = stacky_server.search(fake_request, 'nova') + + self.assertEqual(resp.status_code, 200) + json_resp = json.loads(resp.content) + self._assert_on_search_nova(json_resp, raw) + self.mox.VerifyAll() + def test_search_by_field_for_nova_with_limit(self): search_result = [["#", "?", "When", "Deployment", "Event", "Host", "State", "State'", "Task'"], [1, " ", From a30eabc6010440ec4eda74bd0f5accf252c97fe7 Mon Sep 17 00:00:00 2001 From: Anuj Mathur Date: Tue, 10 Sep 2013 14:26:28 +0530 Subject: [PATCH 3/3] Made owner in ImageUsage and ImageExists nullable since it is possible for the Glance image to have no owner specified. --- ..._nullable_in_imageusage_and_imageexists.py | 228 ++++++++++++++++++ stacktach/models.py | 5 +- 2 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 stacktach/migrations/0007_update_owner_to_nullable_in_imageusage_and_imageexists.py diff --git a/stacktach/migrations/0007_update_owner_to_nullable_in_imageusage_and_imageexists.py b/stacktach/migrations/0007_update_owner_to_nullable_in_imageusage_and_imageexists.py new file mode 100644 index 0000000..891e4ed --- /dev/null +++ b/stacktach/migrations/0007_update_owner_to_nullable_in_imageusage_and_imageexists.py @@ -0,0 +1,228 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'ImageUsage.owner' + db.alter_column(u'stacktach_imageusage', 'owner', self.gf('django.db.models.fields.CharField')(max_length=50, null=True)) + + # Changing field 'ImageExists.owner' + db.alter_column(u'stacktach_imageexists', 'owner', self.gf('django.db.models.fields.CharField')(max_length=255, null=True)) + + def backwards(self, orm): + + # User chose to not deal with backwards NULL issues for 'ImageUsage.owner' + raise RuntimeError("Cannot reverse this migration. 'ImageUsage.owner' and its values cannot be restored.") + + # User chose to not deal with backwards NULL issues for 'ImageExists.owner' + raise RuntimeError("Cannot reverse this migration. 'ImageExists.owner' and its values cannot be restored.") + + models = { + u'stacktach.deployment': { + 'Meta': {'object_name': 'Deployment'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'stacktach.genericrawdata': { + 'Meta': {'object_name': 'GenericRawData'}, + 'deployment': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.Deployment']"}), + 'event': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'host': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '100', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'instance': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'json': ('django.db.models.fields.TextField', [], {}), + 'message_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'publisher': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'request_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'routing_key': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'service': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'tenant': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'when': ('django.db.models.fields.DecimalField', [], {'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}) + }, + u'stacktach.glancerawdata': { + 'Meta': {'object_name': 'GlanceRawData'}, + 'deployment': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.Deployment']"}), + 'event': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'host': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '100', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image_type': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'db_index': 'True'}), + 'instance': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'json': ('django.db.models.fields.TextField', [], {}), + 'owner': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'publisher': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'request_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'routing_key': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'service': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'db_index': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '36', 'null': 'True', 'blank': 'True'}), + 'when': ('django.db.models.fields.DecimalField', [], {'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}) + }, + u'stacktach.imagedeletes': { + 'Meta': {'object_name': 'ImageDeletes'}, + 'deleted_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'raw': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.GlanceRawData']", 'null': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}) + }, + u'stacktach.imageexists': { + 'Meta': {'object_name': 'ImageExists'}, + 'audit_period_beginning': ('django.db.models.fields.DecimalField', [], {'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'audit_period_ending': ('django.db.models.fields.DecimalField', [], {'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'created_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'delete': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': u"orm['stacktach.ImageDeletes']"}), + 'deleted_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'fail_reason': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}), + 'raw': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': u"orm['stacktach.GlanceRawData']"}), + 'send_status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}), + 'size': ('django.db.models.fields.BigIntegerField', [], {'max_length': '20'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '50', 'db_index': 'True'}), + 'usage': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': u"orm['stacktach.ImageUsage']"}), + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}) + }, + u'stacktach.imageusage': { + 'Meta': {'object_name': 'ImageUsage'}, + 'created_at': ('django.db.models.fields.DecimalField', [], {'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_raw': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.GlanceRawData']", 'null': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'db_index': 'True'}), + 'size': ('django.db.models.fields.BigIntegerField', [], {'max_length': '20'}), + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}) + }, + u'stacktach.instancedeletes': { + 'Meta': {'object_name': 'InstanceDeletes'}, + 'deleted_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'instance': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'launched_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'raw': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.RawData']", 'null': 'True'}) + }, + u'stacktach.instanceexists': { + 'Meta': {'object_name': 'InstanceExists'}, + 'audit_period_beginning': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'audit_period_ending': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'delete': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': u"orm['stacktach.InstanceDeletes']"}), + 'deleted_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'fail_reason': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '300', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'instance': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'instance_type_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'launched_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'message_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'os_architecture': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'os_distro': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'os_version': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'raw': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': u"orm['stacktach.RawData']"}), + 'rax_options': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'send_status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'db_index': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '50', 'db_index': 'True'}), + 'tenant': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'usage': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': u"orm['stacktach.InstanceUsage']"}) + }, + u'stacktach.instancereconcile': { + 'Meta': {'object_name': 'InstanceReconcile'}, + 'deleted_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'instance': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'instance_type_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'launched_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'os_architecture': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'os_distro': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'os_version': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'rax_options': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'row_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'row_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'source': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '150', 'null': 'True', 'blank': 'True'}), + 'tenant': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}) + }, + u'stacktach.instanceusage': { + 'Meta': {'object_name': 'InstanceUsage'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'instance': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'instance_type_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'launched_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'os_architecture': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'os_distro': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'os_version': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'rax_options': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'request_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'tenant': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}) + }, + u'stacktach.jsonreport': { + 'Meta': {'object_name': 'JsonReport'}, + 'created': ('django.db.models.fields.DecimalField', [], {'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'json': ('django.db.models.fields.TextField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'period_end': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'period_start': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'version': ('django.db.models.fields.IntegerField', [], {'default': '1'}) + }, + u'stacktach.lifecycle': { + 'Meta': {'object_name': 'Lifecycle'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'instance': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'last_raw': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.RawData']", 'null': 'True'}), + 'last_state': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'last_task_state': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}) + }, + u'stacktach.rawdata': { + 'Meta': {'object_name': 'RawData'}, + 'deployment': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.Deployment']"}), + 'event': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'host': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '100', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image_type': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'db_index': 'True'}), + 'instance': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'json': ('django.db.models.fields.TextField', [], {}), + 'old_state': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '20', 'null': 'True', 'blank': 'True'}), + 'old_task': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '30', 'null': 'True', 'blank': 'True'}), + 'publisher': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'request_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'routing_key': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'service': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '20', 'null': 'True', 'blank': 'True'}), + 'task': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '30', 'null': 'True', 'blank': 'True'}), + 'tenant': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'when': ('django.db.models.fields.DecimalField', [], {'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}) + }, + u'stacktach.rawdataimagemeta': { + 'Meta': {'object_name': 'RawDataImageMeta'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'os_architecture': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'os_distro': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'os_version': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'raw': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.RawData']"}), + 'rax_options': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) + }, + u'stacktach.requesttracker': { + 'Meta': {'object_name': 'RequestTracker'}, + 'completed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'duration': ('django.db.models.fields.DecimalField', [], {'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_timing': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.Timing']", 'null': 'True'}), + 'lifecycle': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.Lifecycle']"}), + 'request_id': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'start': ('django.db.models.fields.DecimalField', [], {'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}) + }, + u'stacktach.timing': { + 'Meta': {'object_name': 'Timing'}, + 'diff': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'end_raw': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': u"orm['stacktach.RawData']"}), + 'end_when': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'lifecycle': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.Lifecycle']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'start_raw': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': u"orm['stacktach.RawData']"}), + 'start_when': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6'}) + } + } + + complete_apps = ['stacktach'] \ No newline at end of file diff --git a/stacktach/models.py b/stacktach/models.py index a34f494..035d1c9 100644 --- a/stacktach/models.py +++ b/stacktach/models.py @@ -75,6 +75,7 @@ class GenericRawData(models.Model): self.instance, self.request_id]) return results + class RawData(models.Model): result_titles = [["#", "?", "When", "Deployment", "Event", "Host", "State", "State'", "Task'"]] @@ -424,7 +425,7 @@ class ImageUsage(models.Model): uuid = models.CharField(max_length=50, db_index=True) created_at = models.DecimalField(max_digits=20, decimal_places=6, db_index=True) - owner = models.CharField(max_length=50, db_index=True) + owner = models.CharField(max_length=50, db_index=True, null=True) size = models.BigIntegerField(max_length=20) last_raw = models.ForeignKey(GlanceRawData, null=True) @@ -476,7 +477,7 @@ class ImageExists(models.Model): usage = models.ForeignKey(ImageUsage, related_name='+', null=True) delete = models.ForeignKey(ImageDeletes, related_name='+', null=True) send_status = models.IntegerField(default=0, db_index=True) - owner = models.CharField(max_length=255, db_index=True) + owner = models.CharField(max_length=255, db_index=True, null=True) size = models.BigIntegerField(max_length=20) def update_status(self, new_status):