Revision 9ad94f0a

b/README.develop
13 13
- pycurl [pycurl==7.19.0]
14 14
- python-dateutil  [python-dateutil==1.4.1]
15 15
  WARNING: version python-dateutil==2.0 downloaded by pip known *not* to work with Python 2.6
16
- south [south==0.7.1]
16 17

  
17 18
also, depending on the database engine of choice, on one of the following:
18 19
- MySQL-python [MySQL-python==1.2.3]
......
27 28
Alternatively, you can use your system's package manager to install
28 29
the dependencies (e.g. Macports has them all).
29 30

  
30
*On Snow Leopard, you have to set the following environment variable for
31
*On Snow Leopard and linux (64-bit), you have to set the following environment variable for
31 32
pip to compile the dependencies correctly. 
32 33

  
33 34
	$export ARCHFLAGS="-arch x86_64"
......
185 186

  
186 187
9. (Hopefully) Done
187 188

  
189
South Database Migration
190
------------------------
191

  
192
First, remember to add the south app to settings.py (it is already included in the
193
settings.py.dist).
194

  
195
In addition, do not use the syncdb management command. Only the first time
196

  
197
The first time (is not required, since when you read this documentation the migration
198
is already active) the migration of the schema will initialised with the following
199
command:
200

  
201
    $ ./bin/python schemamigration db --initial
202

  
203
Each time you make changes to the database and data migration is not required (WARNING: always
204
perform this with extreme care):
205

  
206
    $ ./bin/python schemamigration db --auto
207

  
208
The above will create the migration script. Now this must be applied to the live database.
209

  
210
    $ ./bin/python migrate db
211

  
212
Consider this example (adding a field to the SynnefoUser model):
213

  
214
    bkarak@nefarian:~/devel/synnefo$ ./bin/python manage.py schemamigration db --auto
215
     + Added field new_south_test_field on db.SynnefoUser
216
     Created 0002_auto__add_field_synnefouser_new_south_test_field.py. You can now apply this migration with: ./manage.py migrate db
217
    bkarak@nefarian:~/devel/synnefo$ ./manage.py migrate db
218
     Running migrations for db:
219
     - Migrating forwards to 0002_auto__add_field_synnefouser_new_south_test_field.
220
     > db:0002_auto__add_field_synnefouser_new_south_test_field
221
     - Loading initial data for db.
222
    Installing json fixture 'initial_data' from '/home/bkarak/devel/synnefo/../synnefo/db/fixtures'.
223
    Installed 1 object(s) from 1 fixture(s)
224

  
225
South needs some extra definitions to the model to preserve and migrate the existing data, for example, if we add a field
226
in a model, we should declare its default value. If not, south management will propably fail, after indicating the error.
227

  
228
If we need to do data migration as well, for example rename a field, we use tha 'datamigration' management command.
229

  
188 230
UI Testing
189 231
----------
190 232
The functional ui tests require the Selenium server and the synnefo app to 
b/db/migrations/0002_auto__add_field_synnefouser_new_south_test_field.py
1
# encoding: utf-8
2
import datetime
3
from south.db import db
4
from south.v2 import SchemaMigration
5
from django.db import models
6

  
7
class Migration(SchemaMigration):
8

  
9
    def forwards(self, orm):
10
        
11
        # Adding field 'SynnefoUser.new_south_test_field'
12
        db.add_column('db_synnefouser', 'new_south_test_field', self.gf('django.db.models.fields.IntegerField')(default=1), keep_default=False)
13

  
14

  
15
    def backwards(self, orm):
16
        
17
        # Deleting field 'SynnefoUser.new_south_test_field'
18
        db.delete_column('db_synnefouser', 'new_south_test_field')
19

  
20

  
21
    models = {
22
        'db.debit': {
23
            'Meta': {'object_name': 'Debit'},
24
            'description': ('django.db.models.fields.TextField', [], {}),
25
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
26
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']"}),
27
            'vm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.VirtualMachine']"}),
28
            'when': ('django.db.models.fields.DateTimeField', [], {})
29
        },
30
        'db.disk': {
31
            'Meta': {'object_name': 'Disk'},
32
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
33
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
34
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
35
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']", 'null': 'True', 'blank': 'True'}),
36
            'size': ('django.db.models.fields.PositiveIntegerField', [], {}),
37
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
38
            'vm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.VirtualMachine']", 'null': 'True', 'blank': 'True'})
39
        },
40
        'db.flavor': {
41
            'Meta': {'unique_together': "(('cpu', 'ram', 'disk'),)", 'object_name': 'Flavor'},
42
            'cpu': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
43
            'disk': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
44
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
45
            'ram': ('django.db.models.fields.IntegerField', [], {'default': '0'})
46
        },
47
        'db.flavorcost': {
48
            'Meta': {'object_name': 'FlavorCost'},
49
            'cost_active': ('django.db.models.fields.PositiveIntegerField', [], {}),
50
            'cost_inactive': ('django.db.models.fields.PositiveIntegerField', [], {}),
51
            'effective_from': ('django.db.models.fields.DateTimeField', [], {}),
52
            'flavor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Flavor']"}),
53
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
54
        },
55
        'db.image': {
56
            'Meta': {'object_name': 'Image'},
57
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
58
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
59
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
60
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']", 'null': 'True', 'blank': 'True'}),
61
            'sourcevm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.VirtualMachine']", 'null': 'True'}),
62
            'state': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
63
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
64
        },
65
        'db.imagemetadata': {
66
            'Meta': {'object_name': 'ImageMetadata'},
67
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
68
            'image': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Image']"}),
69
            'meta_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
70
            'meta_value': ('django.db.models.fields.CharField', [], {'max_length': '500'})
71
        },
72
        'db.limit': {
73
            'Meta': {'object_name': 'Limit'},
74
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
75
            'name': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
76
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']"}),
77
            'value': ('django.db.models.fields.IntegerField', [], {})
78
        },
79
        'db.synnefouser': {
80
            'Meta': {'object_name': 'SynnefoUser'},
81
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
82
            'credit': ('django.db.models.fields.IntegerField', [], {}),
83
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
84
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
85
            'new_south_test_field': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
86
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
87
        },
88
        'db.virtualmachine': {
89
            'Meta': {'object_name': 'VirtualMachine'},
90
            'action': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
91
            'backendjobid': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
92
            'backendjobstatus': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
93
            'backendlogmsg': ('django.db.models.fields.TextField', [], {'null': 'True'}),
94
            'backendopcode': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
95
            'charged': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 4, 11, 9, 13, 47, 902166)'}),
96
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
97
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
98
            'flavor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Flavor']"}),
99
            'hostid': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
100
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
101
            'ipfour': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}),
102
            'ipsix': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
103
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
104
            'operstate': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
105
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']"}),
106
            'sourceimage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Image']"}),
107
            'suspended': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
108
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
109
        },
110
        'db.virtualmachinegroup': {
111
            'Meta': {'object_name': 'VirtualMachineGroup'},
112
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
113
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
114
            'machines': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['db.VirtualMachine']", 'symmetrical': 'False'}),
115
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
116
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']"}),
117
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
118
        },
119
        'db.virtualmachinemetadata': {
120
            'Meta': {'object_name': 'VirtualMachineMetadata'},
121
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
122
            'meta_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
123
            'meta_value': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
124
            'vm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.VirtualMachine']"})
125
        }
126
    }
127

  
128
    complete_apps = ['db']
b/db/models.py
10 10
    credit = models.IntegerField('Credit Balance')
11 11
    created = models.DateTimeField('Time of creation', auto_now_add=True)
12 12
    updated = models.DateTimeField('Time of last update', auto_now=True)
13
    new_south_test_field = models.IntegerField(default=1)
13 14

  
14 15
    class Meta:
15 16
        verbose_name = u'Synnefo User'

Also available in: Unified diff