← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~rvb/maas/maas-south into lp:maas

 

Raphaël Badin has proposed merging lp:~rvb/maas/maas-south into lp:maas.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~rvb/maas/maas-south/+merge/97840

This branch uses South to manage database migrations in MAAS.  The files in src/*/migrations/ (except from the header) are autogenerated by South (they simply correspond to a copy - in the South format - of the current db structure), they were generated by running "./bin/maas schemamigration maasserver --initial && ./bin/maas schemamigration metadataserver --initial".

I'm planning to wait for a consensus on whether or not our db structure is ok for 12.04 before landing this.  If we have to make changes to the db we will have to modifiy this branch:
- remove the file in src/*/migrations/
- run "./bin/maas schemamigration maasserver --initial && ./bin/maas schemamigration metadataserver --initial"
- add back the header in src/*/migrations/

-- 
https://code.launchpad.net/~rvb/maas/maas-south/+merge/97840
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~rvb/maas/maas-south into lp:maas.
=== modified file 'HACKING.txt'
--- HACKING.txt	2012-03-15 22:43:55 +0000
+++ HACKING.txt	2012-03-16 10:57:27 +0000
@@ -39,7 +39,7 @@
         python-django-south python-twisted python-txamqp python-amqplib \
         python-formencode python-oauth python-oops python-oops-datedir-repo \
         python-twisted python-oops-wsgi python-oops-twisted \
-        python-psycopg2 python-yaml python-convoy
+        python-psycopg2 python-yaml python-convoy python-django-south
 
 Additionally, you need to install the following python libraries
 for development convenience::

=== modified file 'Makefile'
--- Makefile	2012-03-15 22:43:55 +0000
+++ Makefile	2012-03-16 10:57:27 +0000
@@ -108,7 +108,7 @@
 	bin/maas shell --settings=maas.demo
 
 syncdb: bin/maas dev-db
-	bin/maas syncdb --noinput
+	bin/maas syncdb --noinput --migrate
 
 checkbox: config=checkbox/plugins/jobs_info/directories=$(PWD)/qa/checkbox
 checkbox:

=== modified file 'src/maas/settings.py'
--- src/maas/settings.py	2012-03-15 17:30:53 +0000
+++ src/maas/settings.py	2012-03-16 10:57:27 +0000
@@ -236,6 +236,7 @@
     'maasserver',
     'metadataserver',
     'piston',
+    'south',
 )
 
 if DEBUG:

=== modified file 'src/maasserver/fields.py'
--- src/maasserver/fields.py	2012-02-29 19:00:41 +0000
+++ src/maasserver/fields.py	2012-03-16 10:57:27 +0000
@@ -28,6 +28,7 @@
     )
 from django.forms import RegexField
 import psycopg2.extensions
+from south.modelsinspector import add_introspection_rules
 
 
 mac_re = re.compile(r'^([0-9a-fA-F]{2}[:-]){5}[0-9a-fA-F]{2}$')
@@ -39,6 +40,17 @@
 validate_mac = RegexValidator(regex=mac_re, message=mac_error_msg)
 
 
+# The MACAddressField and the JSONObjectField don't introduce any new
+# parameter compared to their parent's constructors so South will handle
+# them just fine.
+# See http://south.aeracode.org/docs/customfields.html#extending-introspection
+# for details.
+add_introspection_rules(
+    [],
+    ["^maasserver\.fields\.MACAddressField",
+     "^maasserver\.fields\.JSONObjectField"])
+
+
 class MACAddressFormField(RegexField):
 
     def __init__(self, *args, **kwargs):

=== added directory 'src/maasserver/migrations'
=== added file 'src/maasserver/migrations/0001_initial.py'
--- src/maasserver/migrations/0001_initial.py	1970-01-01 00:00:00 +0000
+++ src/maasserver/migrations/0001_initial.py	2012-03-16 10:57:27 +0000
@@ -0,0 +1,190 @@
+# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Initial maasserver migration."""
+
+from __future__ import (
+    print_function,
+    # This breaks South.
+    #unicode_literals,
+    )
+
+__metaclass__ = type
+__all__ = []
+
+# flake8: noqa
+# SKIP this file when reformatting.
+# The rest of this file was generated by South.
+
+# encoding: 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):
+        
+        # Adding model 'Node'
+        db.create_table('maasserver_node', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('created', self.gf('django.db.models.fields.DateField')()),
+            ('updated', self.gf('django.db.models.fields.DateTimeField')()),
+            ('system_id', self.gf('django.db.models.fields.CharField')(default=u'node-abe5fcd0-6f3a-11e1-b5bf-00219bd0a2de', unique=True, max_length=41)),
+            ('hostname', self.gf('django.db.models.fields.CharField')(default=u'', max_length=255, blank=True)),
+            ('status', self.gf('django.db.models.fields.IntegerField')(default=0, max_length=10)),
+            ('owner', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['auth.User'], null=True, blank=True)),
+            ('after_commissioning_action', self.gf('django.db.models.fields.IntegerField')(default=0)),
+            ('architecture', self.gf('django.db.models.fields.CharField')(default=u'i386', max_length=10)),
+            ('power_type', self.gf('django.db.models.fields.CharField')(default=u'', max_length=10, blank=True)),
+        ))
+        db.send_create_signal('maasserver', ['Node'])
+
+        # Adding model 'MACAddress'
+        db.create_table('maasserver_macaddress', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('created', self.gf('django.db.models.fields.DateField')()),
+            ('updated', self.gf('django.db.models.fields.DateTimeField')()),
+            ('mac_address', self.gf('maasserver.fields.MACAddressField')()),
+            ('node', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['maasserver.Node'])),
+        ))
+        db.send_create_signal('maasserver', ['MACAddress'])
+
+        # Adding model 'UserProfile'
+        db.create_table('maasserver_userprofile', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('user', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True)),
+        ))
+        db.send_create_signal('maasserver', ['UserProfile'])
+
+        # Adding model 'SSHKeys'
+        db.create_table('maasserver_sshkeys', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['maasserver.UserProfile'])),
+            ('key', self.gf('django.db.models.fields.TextField')()),
+        ))
+        db.send_create_signal('maasserver', ['SSHKeys'])
+
+        # Adding model 'FileStorage'
+        db.create_table('maasserver_filestorage', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('filename', self.gf('django.db.models.fields.CharField')(unique=True, max_length=100)),
+            ('data', self.gf('django.db.models.fields.files.FileField')(max_length=255)),
+        ))
+        db.send_create_signal('maasserver', ['FileStorage'])
+
+        # Adding model 'Config'
+        db.create_table('maasserver_config', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('value', self.gf('maasserver.fields.JSONObjectField')(null=True)),
+        ))
+        db.send_create_signal('maasserver', ['Config'])
+
+
+    def backwards(self, orm):
+        
+        # Deleting model 'Node'
+        db.delete_table('maasserver_node')
+
+        # Deleting model 'MACAddress'
+        db.delete_table('maasserver_macaddress')
+
+        # Deleting model 'UserProfile'
+        db.delete_table('maasserver_userprofile')
+
+        # Deleting model 'SSHKeys'
+        db.delete_table('maasserver_sshkeys')
+
+        # Deleting model 'FileStorage'
+        db.delete_table('maasserver_filestorage')
+
+        # Deleting model 'Config'
+        db.delete_table('maasserver_config')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'maasserver.config': {
+            'Meta': {'object_name': 'Config'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'})
+        },
+        'maasserver.filestorage': {
+            'Meta': {'object_name': 'FileStorage'},
+            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '255'}),
+            'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'maasserver.macaddress': {
+            'Meta': {'object_name': 'MACAddress'},
+            'created': ('django.db.models.fields.DateField', [], {}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'mac_address': ('maasserver.fields.MACAddressField', [], {}),
+            'node': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['maasserver.Node']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {})
+        },
+        'maasserver.node': {
+            'Meta': {'object_name': 'Node'},
+            'after_commissioning_action': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'architecture': ('django.db.models.fields.CharField', [], {'default': "u'i386'", 'max_length': '10'}),
+            'created': ('django.db.models.fields.DateField', [], {}),
+            'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}),
+            'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}),
+            'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-abe7a9cc-6f3a-11e1-b5bf-00219bd0a2de'", 'unique': 'True', 'max_length': '41'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {})
+        },
+        'maasserver.sshkeys': {
+            'Meta': {'object_name': 'SSHKeys'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.TextField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['maasserver.UserProfile']"})
+        },
+        'maasserver.userprofile': {
+            'Meta': {'object_name': 'UserProfile'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
+        }
+    }
+
+    complete_apps = ['maasserver']

=== added file 'src/maasserver/migrations/__init__.py'
=== modified file 'src/maastesting/testcase.py'
--- src/maastesting/testcase.py	2012-03-13 05:34:38 +0000
+++ src/maastesting/testcase.py	2012-03-16 10:57:27 +0000
@@ -17,7 +17,7 @@
 import unittest
 
 from django.conf import settings
-from django.core.management import call_command
+from django.core.management.commands.syncdb import Command
 from django.db.models import loading
 import django.test
 import testresources
@@ -81,7 +81,8 @@
         assert self.app is not None, "TestCase.app must be defined!"
         settings.INSTALLED_APPS.append(self.app)
         loading.cache.loaded = False
-        call_command('syncdb', interactive=False, verbosity=0)
+        # Use Django's 'syncdb' rather than South's.
+        Command().handle_noargs(verbosity=0, interactive=False)
         super(TestModelTestCase, self)._pre_setup()
 
     def _post_teardown(self):

=== modified file 'src/metadataserver/fields.py'
--- src/metadataserver/fields.py	2012-02-29 17:31:38 +0000
+++ src/metadataserver/fields.py	2012-03-16 10:57:27 +0000
@@ -22,6 +22,7 @@
     Field,
     SubfieldBase,
     )
+from south.modelsinspector import add_introspection_rules
 
 
 class Bin(bytes):
@@ -52,6 +53,13 @@
         super(Bin, self).__init__(initializer)
 
 
+# The BinaryField does not introduce any new parameter compared to its
+# parent's constructor so South will handle it just fine.
+# See http://south.aeracode.org/docs/customfields.html#extending-introspection
+# for details.
+add_introspection_rules([], ["^metadataserver\.fields\.BinaryField"])
+
+
 class BinaryField(Field):
     """A field that stores binary data.
 

=== added directory 'src/metadataserver/migrations'
=== added file 'src/metadataserver/migrations/0001_initial.py'
--- src/metadataserver/migrations/0001_initial.py	1970-01-01 00:00:00 +0000
+++ src/metadataserver/migrations/0001_initial.py	2012-03-16 10:57:27 +0000
@@ -0,0 +1,145 @@
+# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Initial metadataserver migration."""
+
+from __future__ import (
+    print_function,
+    # This breaks South.
+    #unicode_literals,
+    )
+
+__metaclass__ = type
+__all__ = []
+
+# flake8: noqa
+# SKIP this file when reformatting.
+# The rest of this file was generated by South.
+
+# encoding: 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):
+        
+        # Adding model 'NodeKey'
+        db.create_table('metadataserver_nodekey', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('node', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['maasserver.Node'], unique=True)),
+            ('token', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['piston.Token'], unique=True)),
+            ('key', self.gf('django.db.models.fields.CharField')(unique=True, max_length=18)),
+        ))
+        db.send_create_signal('metadataserver', ['NodeKey'])
+
+        # Adding model 'NodeUserData'
+        db.create_table('metadataserver_nodeuserdata', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('node', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['maasserver.Node'], unique=True)),
+            ('data', self.gf('metadataserver.fields.BinaryField')()),
+        ))
+        db.send_create_signal('metadataserver', ['NodeUserData'])
+
+
+    def backwards(self, orm):
+        
+        # Deleting model 'NodeKey'
+        db.delete_table('metadataserver_nodekey')
+
+        # Deleting model 'NodeUserData'
+        db.delete_table('metadataserver_nodeuserdata')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'maasserver.node': {
+            'Meta': {'object_name': 'Node'},
+            'after_commissioning_action': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'architecture': ('django.db.models.fields.CharField', [], {'default': "u'i386'", 'max_length': '10'}),
+            'created': ('django.db.models.fields.DateField', [], {}),
+            'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}),
+            'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}),
+            'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-b5811680-6f3a-11e1-baa5-00219bd0a2de'", 'unique': 'True', 'max_length': '41'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {})
+        },
+        'metadataserver.nodekey': {
+            'Meta': {'object_name': 'NodeKey'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}),
+            'node': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['maasserver.Node']", 'unique': 'True'}),
+            'token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['piston.Token']", 'unique': 'True'})
+        },
+        'metadataserver.nodeuserdata': {
+            'Meta': {'object_name': 'NodeUserData'},
+            'data': ('metadataserver.fields.BinaryField', [], {}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'node': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['maasserver.Node']", 'unique': 'True'})
+        },
+        'piston.consumer': {
+            'Meta': {'object_name': 'Consumer'},
+            'description': ('django.db.models.fields.TextField', [], {}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+            'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': "orm['auth.User']"})
+        },
+        'piston.token': {
+            'Meta': {'object_name': 'Token'},
+            'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['piston.Consumer']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
+            'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+            'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1331883374L'}),
+            'token_type': ('django.db.models.fields.IntegerField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': "orm['auth.User']"}),
+            'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'})
+        }
+    }
+
+    complete_apps = ['metadataserver']

=== added file 'src/metadataserver/migrations/__init__.py'

Follow ups