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): diff --git a/stacktach/stacky_server.py b/stacktach/stacky_server.py index f43b087..296b9fb 100644 --- a/stacktach/stacky_server.py +++ b/stacktach/stacky_server.py @@ -14,6 +14,70 @@ 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 _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() @@ -37,8 +101,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: @@ -113,13 +179,17 @@ def do_uuid(request): 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.select_related().filter(**param).order_by('when') + 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) routing_key_status = routing_key_type(event.routing_key) @@ -132,24 +202,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)) @@ -166,9 +244,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 @@ -195,8 +278,10 @@ 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} + _add_when_filters(request, filters) + events = model_search(request, model, filters, order_by='when') results = [["#", "?", "When", "Deployment", "Event", "Host", "State", "State'", "Task'"]] for e in events: @@ -389,10 +474,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"]] @@ -415,10 +501,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"]] @@ -444,10 +531,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"]] @@ -473,8 +561,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, @@ -493,20 +585,16 @@ def do_jsonreport(request, report_id): def search(request): - DEFAULT = 1000 service = str(request.GET.get('service', 'nova')) 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} + filters = {field: value} + _add_when_filters(request, filters) results = [] try: - events = model.filter(**filter_para) - event_len = len(events) - if event_len > limit: - events = events[0:limit] + + 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 2e6c3f7..c76ce31 100644 --- a/tests/unit/test_stacky_server.py +++ b/tests/unit/test_stacky_server.py @@ -132,10 +132,13 @@ class StackyServerTestCase(StacktachBaseTestCase): 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() @@ -147,7 +150,8 @@ class StackyServerTestCase(StacktachBaseTestCase): 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)']) @@ -155,10 +159,13 @@ class StackyServerTestCase(StacktachBaseTestCase): 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() @@ -170,7 +177,8 @@ class StackyServerTestCase(StacktachBaseTestCase): 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)']) @@ -178,10 +186,13 @@ class StackyServerTestCase(StacktachBaseTestCase): 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() @@ -192,7 +203,8 @@ class StackyServerTestCase(StacktachBaseTestCase): 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)']) @@ -273,6 +285,42 @@ class StackyServerTestCase(StacktachBaseTestCase): 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() + + 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_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() @@ -303,6 +351,43 @@ class StackyServerTestCase(StacktachBaseTestCase): 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() + + 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", + "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_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', + 'service': 'glance'} + 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() @@ -367,6 +452,7 @@ class StackyServerTestCase(StacktachBaseTestCase): 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() @@ -386,9 +472,9 @@ class StackyServerTestCase(StacktachBaseTestCase): 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() @@ -398,6 +484,7 @@ class StackyServerTestCase(StacktachBaseTestCase): 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() @@ -417,9 +504,9 @@ class StackyServerTestCase(StacktachBaseTestCase): 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() @@ -429,6 +516,7 @@ class StackyServerTestCase(StacktachBaseTestCase): 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() @@ -450,10 +538,10 @@ class StackyServerTestCase(StacktachBaseTestCase): '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() @@ -463,6 +551,7 @@ class StackyServerTestCase(StacktachBaseTestCase): 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() @@ -495,6 +584,7 @@ class StackyServerTestCase(StacktachBaseTestCase): 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() @@ -516,6 +606,43 @@ class StackyServerTestCase(StacktachBaseTestCase): 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() + + 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_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() @@ -912,6 +1039,7 @@ class StackyServerTestCase(StacktachBaseTestCase): 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() @@ -938,6 +1066,7 @@ class StackyServerTestCase(StacktachBaseTestCase): 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() @@ -978,6 +1107,7 @@ class StackyServerTestCase(StacktachBaseTestCase): 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() @@ -1004,6 +1134,7 @@ class StackyServerTestCase(StacktachBaseTestCase): 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() @@ -1047,6 +1178,7 @@ class StackyServerTestCase(StacktachBaseTestCase): 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() @@ -1077,6 +1209,7 @@ class StackyServerTestCase(StacktachBaseTestCase): 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() @@ -1157,6 +1290,29 @@ class StackyServerTestCase(StacktachBaseTestCase): 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) + + 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, " ", @@ -1188,4 +1344,91 @@ class StackyServerTestCase(StacktachBaseTestCase): 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