Merge lp:~rvb/maas/add-space-subnet into lp:~maas-committers/maas/trunk

Proposed by Raphaël Badin
Status: Merged
Approved by: Raphaël Badin
Approved revision: no longer in the source branch.
Merged at revision: 4031
Proposed branch: lp:~rvb/maas/add-space-subnet
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 1583 lines (+1396/-6)
13 files modified
src/maasserver/fields.py (+4/-3)
src/maasserver/migrations/0142_add_space_subnet.py (+532/-0)
src/maasserver/migrations/0143_create_default_space.py (+501/-0)
src/maasserver/models/__init__.py (+4/-0)
src/maasserver/models/space.py (+80/-0)
src/maasserver/models/subnet.py (+94/-0)
src/maasserver/models/tests/test_space.py (+69/-0)
src/maasserver/models/tests/test_subnet.py (+54/-0)
src/maasserver/models/tests/test_vlan.py (+9/-0)
src/maasserver/models/vlan.py (+7/-0)
src/maasserver/testing/factory.py (+32/-0)
src/maasserver/tests/test_fields.py (+3/-3)
src/maastesting/factory.py (+7/-0)
To merge this branch: bzr merge lp:~rvb/maas/add-space-subnet
Reviewer Review Type Date Requested Status
Mike Pontillo (community) Approve
Review via email: mp+262431@code.launchpad.net

Commit message

Add space and subnet models.

Description of the change

This branch contains:
- The Space model
- The Subnet model
- The schema migration to create the Space and Subnet tables
- The data migration to create the default Space object

Couple of notes:
- I had to refactor the CIDRField field; Django tries to be clever when an object is 'iterable' and thus was breaking when performing lookups on IPNetwork objects.
- I kept the Subnets unique=True for now although we hope to relax this restriction in the future.

To post a comment you must log in.
Revision history for this message
Mike Pontillo (mpontillo) wrote :

I haven't done a full review (yet) but wanted to submit a few comments below.

Revision history for this message
Raphaël Badin (rvb) :
Revision history for this message
Mike Pontillo (mpontillo) wrote :

Looks good. Just one comment regarding the gateway_ip below. (Will it *always* be required?)

review: Approve
Revision history for this message
Raphaël Badin (rvb) wrote :

Thanks for the review. I've update the gateway_ip definition.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/fields.py'
2--- src/maasserver/fields.py 2015-06-17 14:52:20 +0000
3+++ src/maasserver/fields.py 2015-06-22 16:52:47 +0000
4@@ -104,6 +104,7 @@
5 "^maasserver\.fields\.EditableBinaryField",
6 "^maasserver\.fields\.MAASIPAddressField",
7 "^maasserver\.fields\.LargeObjectField",
8+ "^maasserver\.fields\.CIDRField",
9 ])
10
11
12@@ -541,7 +542,7 @@
13
14 def parse_cidr(value):
15 try:
16- return IPNetwork(value)
17+ return unicode(IPNetwork(value).cidr)
18 except AddrFormatError as e:
19 raise ValidationError(e.message)
20
21@@ -555,7 +556,7 @@
22 return 'cidr'
23
24 def get_prep_value(self, value):
25- return unicode(value.cidr)
26+ return parse_cidr(value)
27
28 def from_db_value(self, value, expression, connection, context):
29 if value is None:
30@@ -564,7 +565,7 @@
31
32 def to_python(self, value):
33 if isinstance(value, IPNetwork):
34- return value
35+ return unicode(value)
36 if not value:
37 return value
38 return parse_cidr(value)
39
40=== added file 'src/maasserver/migrations/0142_add_space_subnet.py'
41--- src/maasserver/migrations/0142_add_space_subnet.py 1970-01-01 00:00:00 +0000
42+++ src/maasserver/migrations/0142_add_space_subnet.py 2015-06-22 16:52:47 +0000
43@@ -0,0 +1,532 @@
44+from django.db import models
45+from south.db import db
46+# -*- coding: utf-8 -*-
47+from south.utils import datetime_utils as datetime
48+from south.v2 import SchemaMigration
49+
50+
51+class Migration(SchemaMigration):
52+
53+ def forwards(self, orm):
54+ # Adding model 'Space'
55+ db.create_table(u'maasserver_space', (
56+ (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
57+ ('created', self.gf('django.db.models.fields.DateTimeField')()),
58+ ('updated', self.gf('django.db.models.fields.DateTimeField')()),
59+ ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=256)),
60+ ))
61+ db.send_create_signal(u'maasserver', ['Space'])
62+
63+ # Adding model 'Subnet'
64+ db.create_table(u'maasserver_subnet', (
65+ (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
66+ ('created', self.gf('django.db.models.fields.DateTimeField')()),
67+ ('updated', self.gf('django.db.models.fields.DateTimeField')()),
68+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
69+ ('vlan', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['maasserver.VLAN'], default=0, on_delete=models.PROTECT)),
70+ ('space', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['maasserver.Space'], default=0, on_delete=models.PROTECT)),
71+ ('cidr', self.gf('maasserver.fields.CIDRField')(unique=True)),
72+ ('gateway_ip', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39, null=True, blank=True)),
73+ ('dns_servers', self.gf('djorm_pgarray.fields.ArrayField')(default={}, dbtype=u'text', null=True, blank=True)),
74+ ))
75+ db.send_create_signal(u'maasserver', ['Subnet'])
76+
77+ # Adding unique constraint on 'Subnet', fields ['name', 'space']
78+ db.create_unique(u'maasserver_subnet', ['name', 'space_id'])
79+
80+
81+ def backwards(self, orm):
82+ # Removing unique constraint on 'Subnet', fields ['name', 'space']
83+ db.delete_unique(u'maasserver_subnet', ['name', 'space_id'])
84+
85+ # Deleting model 'Space'
86+ db.delete_table(u'maasserver_space')
87+
88+ # Deleting model 'Subnet'
89+ db.delete_table(u'maasserver_subnet')
90+
91+
92+ models = {
93+ u'auth.group': {
94+ 'Meta': {'object_name': 'Group'},
95+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
96+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
97+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
98+ },
99+ u'auth.permission': {
100+ 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
101+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
102+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
103+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
104+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
105+ },
106+ u'auth.user': {
107+ 'Meta': {'object_name': 'User'},
108+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
109+ 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}),
110+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
111+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
112+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
113+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
114+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
115+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
116+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
117+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
118+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
119+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
120+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
121+ },
122+ u'contenttypes.contenttype': {
123+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
124+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
125+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
126+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
127+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
128+ },
129+ u'maasserver.blockdevice': {
130+ 'Meta': {'ordering': "[u'id']", 'unique_together': "((u'node', u'path'),)", 'object_name': 'BlockDevice'},
131+ 'block_size': ('django.db.models.fields.IntegerField', [], {}),
132+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
133+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
134+ 'id_path': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
135+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
136+ 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}),
137+ 'path': ('django.db.models.fields.FilePathField', [], {'max_length': '100'}),
138+ 'size': ('django.db.models.fields.BigIntegerField', [], {}),
139+ 'tags': ('djorm_pgarray.fields.ArrayField', [], {'default': '[]', 'dbtype': "u'text'", 'blank': 'True'}),
140+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
141+ },
142+ u'maasserver.bootresource': {
143+ 'Meta': {'unique_together': "((u'name', u'architecture'),)", 'object_name': 'BootResource'},
144+ 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
145+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
146+ 'extra': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
147+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
148+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
149+ 'rtype': ('django.db.models.fields.IntegerField', [], {'max_length': '10'}),
150+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
151+ },
152+ u'maasserver.bootresourcefile': {
153+ 'Meta': {'unique_together': "((u'resource_set', u'filetype'),)", 'object_name': 'BootResourceFile'},
154+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
155+ 'extra': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
156+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
157+ 'filetype': ('django.db.models.fields.CharField', [], {'default': "u'root-tgz'", 'max_length': '20'}),
158+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
159+ 'largefile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.LargeFile']"}),
160+ 'resource_set': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'files'", 'to': u"orm['maasserver.BootResourceSet']"}),
161+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
162+ },
163+ u'maasserver.bootresourceset': {
164+ 'Meta': {'unique_together': "((u'resource', u'version'),)", 'object_name': 'BootResourceSet'},
165+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
166+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
167+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
168+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'sets'", 'to': u"orm['maasserver.BootResource']"}),
169+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
170+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '255'})
171+ },
172+ u'maasserver.bootsource': {
173+ 'Meta': {'object_name': 'BootSource'},
174+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
175+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
176+ 'keyring_data': ('maasserver.fields.EditableBinaryField', [], {'blank': 'True'}),
177+ 'keyring_filename': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
178+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
179+ 'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200'})
180+ },
181+ u'maasserver.bootsourcecache': {
182+ 'Meta': {'object_name': 'BootSourceCache'},
183+ 'arch': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
184+ 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}),
185+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
186+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
187+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
188+ 'os': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
189+ 'release': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
190+ 'subarch': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
191+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
192+ },
193+ u'maasserver.bootsourceselection': {
194+ 'Meta': {'unique_together': "((u'boot_source', u'os', u'release'),)", 'object_name': 'BootSourceSelection'},
195+ 'arches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
196+ 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}),
197+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
198+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
199+ 'labels': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
200+ 'os': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
201+ 'release': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
202+ 'subarches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
203+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
204+ },
205+ u'maasserver.candidatename': {
206+ 'Meta': {'unique_together': "((u'name', u'position'),)", 'object_name': 'CandidateName'},
207+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
208+ 'name': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
209+ 'position': ('django.db.models.fields.IntegerField', [], {})
210+ },
211+ u'maasserver.componenterror': {
212+ 'Meta': {'object_name': 'ComponentError'},
213+ 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}),
214+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
215+ 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
216+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
217+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
218+ },
219+ u'maasserver.config': {
220+ 'Meta': {'object_name': 'Config'},
221+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
222+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
223+ 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'})
224+ },
225+ u'maasserver.dhcplease': {
226+ 'Meta': {'object_name': 'DHCPLease'},
227+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
228+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
229+ 'mac': ('maasserver.fields.MACAddressField', [], {}),
230+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"})
231+ },
232+ u'maasserver.downloadprogress': {
233+ 'Meta': {'object_name': 'DownloadProgress'},
234+ 'bytes_downloaded': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
235+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
236+ 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}),
237+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
238+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
239+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
240+ 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
241+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
242+ },
243+ u'maasserver.event': {
244+ 'Meta': {'object_name': 'Event'},
245+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
246+ 'description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}),
247+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
248+ 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}),
249+ 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.EventType']"}),
250+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
251+ },
252+ u'maasserver.eventtype': {
253+ 'Meta': {'object_name': 'EventType'},
254+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
255+ 'description': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
256+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
257+ 'level': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
258+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
259+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
260+ },
261+ u'maasserver.fabric': {
262+ 'Meta': {'object_name': 'Fabric'},
263+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
264+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
265+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
266+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
267+ },
268+ u'maasserver.filestorage': {
269+ 'Meta': {'unique_together': "((u'filename', u'owner'),)", 'object_name': 'FileStorage'},
270+ 'content': ('metadataserver.fields.BinaryField', [], {'blank': 'True'}),
271+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
272+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
273+ 'key': ('django.db.models.fields.CharField', [], {'default': "u'bc8c7430-18fe-11e5-8aa3-3c970e0e56dc'", 'unique': 'True', 'max_length': '36'}),
274+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
275+ },
276+ u'maasserver.filesystem': {
277+ 'Meta': {'object_name': 'Filesystem'},
278+ 'block_device': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BlockDevice']", 'null': 'True', 'blank': 'True'}),
279+ 'create_params': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
280+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
281+ 'filesystem_group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'filesystems'", 'null': 'True', 'to': u"orm['maasserver.FilesystemGroup']"}),
282+ 'fstype': ('django.db.models.fields.CharField', [], {'default': "u'ext4'", 'max_length': '20'}),
283+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
284+ 'mount_params': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
285+ 'mount_point': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
286+ 'partition': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Partition']", 'null': 'True', 'blank': 'True'}),
287+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
288+ 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
289+ },
290+ u'maasserver.filesystemgroup': {
291+ 'Meta': {'object_name': 'FilesystemGroup'},
292+ 'create_params': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
293+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
294+ 'group_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
295+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
296+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
297+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
298+ 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
299+ },
300+ u'maasserver.interface': {
301+ 'Meta': {'ordering': "(u'created',)", 'object_name': 'Interface'},
302+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
303+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
304+ 'ipv4_params': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
305+ 'ipv6_params': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
306+ 'mac': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.MACAddress']", 'null': 'True', 'blank': 'True'}),
307+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
308+ 'params': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
309+ 'parents': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['maasserver.Interface']", 'null': 'True', 'through': u"orm['maasserver.InterfaceRelationship']", 'blank': 'True'}),
310+ 'tags': ('djorm_pgarray.fields.ArrayField', [], {'default': '[]', 'dbtype': "u'text'", 'blank': 'True'}),
311+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
312+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
313+ 'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.VLAN']", 'on_delete': 'models.PROTECT'})
314+ },
315+ u'maasserver.interfacerelationship': {
316+ 'Meta': {'object_name': 'InterfaceRelationship'},
317+ 'child': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'parent_relationships'", 'to': u"orm['maasserver.Interface']"}),
318+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
319+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
320+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'children_relationships'", 'to': u"orm['maasserver.Interface']"}),
321+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
322+ },
323+ u'maasserver.largefile': {
324+ 'Meta': {'object_name': 'LargeFile'},
325+ 'content': ('maasserver.fields.LargeObjectField', [], {}),
326+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
327+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
328+ 'sha256': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}),
329+ 'total_size': ('django.db.models.fields.BigIntegerField', [], {}),
330+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
331+ },
332+ u'maasserver.licensekey': {
333+ 'Meta': {'unique_together': "((u'osystem', u'distro_series'),)", 'object_name': 'LicenseKey'},
334+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
335+ 'distro_series': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
336+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
337+ 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
338+ 'osystem': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
339+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
340+ },
341+ u'maasserver.macaddress': {
342+ 'Meta': {'ordering': "(u'created',)", 'object_name': 'MACAddress'},
343+ 'cluster_interface': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['maasserver.NodeGroupInterface']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
344+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
345+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
346+ 'ip_addresses': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.StaticIPAddress']", 'symmetrical': 'False', 'through': u"orm['maasserver.MACStaticIPAddressLink']", 'blank': 'True'}),
347+ 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}),
348+ 'networks': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Network']", 'symmetrical': 'False', 'blank': 'True'}),
349+ 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']", 'null': 'True', 'blank': 'True'}),
350+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
351+ },
352+ u'maasserver.macstaticipaddresslink': {
353+ 'Meta': {'unique_together': "((u'ip_address', u'mac_address'),)", 'object_name': 'MACStaticIPAddressLink'},
354+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
355+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
356+ 'ip_address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.StaticIPAddress']", 'unique': 'True'}),
357+ 'mac_address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.MACAddress']"}),
358+ 'nic_alias': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
359+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
360+ },
361+ u'maasserver.network': {
362+ 'Meta': {'object_name': 'Network'},
363+ 'default_gateway': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
364+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
365+ 'dns_servers': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
366+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
367+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
368+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
369+ 'netmask': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39'}),
370+ 'vlan_tag': ('django.db.models.fields.PositiveSmallIntegerField', [], {'unique': 'True', 'null': 'True', 'blank': 'True'})
371+ },
372+ u'maasserver.node': {
373+ 'Meta': {'object_name': 'Node'},
374+ 'agent_name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
375+ 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '31', 'null': 'True', 'blank': 'True'}),
376+ 'boot_type': ('django.db.models.fields.CharField', [], {'default': "u'fastpath'", 'max_length': '20'}),
377+ 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
378+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
379+ 'disable_ipv4': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
380+ 'distro_series': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
381+ 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
382+ 'error_description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}),
383+ 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}),
384+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
385+ 'installable': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
386+ 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}),
387+ 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
388+ 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
389+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}),
390+ 'osystem': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
391+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
392+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "u'children'", 'null': 'True', 'blank': 'True', 'to': u"orm['maasserver.Node']"}),
393+ 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
394+ 'power_state': ('django.db.models.fields.CharField', [], {'default': "u'unknown'", 'max_length': '10'}),
395+ 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}),
396+ 'pxe_mac': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': u"orm['maasserver.MACAddress']", 'blank': 'True', 'null': 'True'}),
397+ 'routers': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'macaddr'", 'null': 'True', 'blank': 'True'}),
398+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}),
399+ 'swap_size': ('django.db.models.fields.BigIntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
400+ 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-bc8b6b3a-18fe-11e5-8aa3-3c970e0e56dc'", 'unique': 'True', 'max_length': '41'}),
401+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}),
402+ 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'null': 'True'}),
403+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
404+ 'zone': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Zone']", 'on_delete': 'models.SET_DEFAULT'})
405+ },
406+ u'maasserver.nodegroup': {
407+ 'Meta': {'object_name': 'NodeGroup'},
408+ 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}),
409+ 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'unique': 'True'}),
410+ 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}),
411+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
412+ 'default_disable_ipv4': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
413+ 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
414+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
415+ 'maas_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
416+ 'name': ('maasserver.models.nodegroup.DomainNameField', [], {'max_length': '80', 'blank': 'True'}),
417+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
418+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
419+ 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
420+ },
421+ u'maasserver.nodegroupinterface': {
422+ 'Meta': {'unique_together': "((u'nodegroup', u'name'),)", 'object_name': 'NodeGroupInterface'},
423+ 'broadcast_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
424+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
425+ 'foreign_dhcp_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
426+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
427+ 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
428+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39'}),
429+ 'ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
430+ 'ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
431+ 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
432+ 'name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
433+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
434+ 'router_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
435+ 'static_ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
436+ 'static_ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
437+ 'subnet_mask': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
438+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
439+ 'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.VLAN']", 'on_delete': 'models.PROTECT'})
440+ },
441+ u'maasserver.partition': {
442+ 'Meta': {'object_name': 'Partition'},
443+ 'bootable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
444+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
445+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
446+ 'partition_table': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'partitions'", 'to': u"orm['maasserver.PartitionTable']"}),
447+ 'size': ('django.db.models.fields.BigIntegerField', [], {}),
448+ 'start_offset': ('django.db.models.fields.BigIntegerField', [], {}),
449+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
450+ 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '36', 'unique': 'True', 'null': 'True', 'blank': 'True'})
451+ },
452+ u'maasserver.partitiontable': {
453+ 'Meta': {'object_name': 'PartitionTable'},
454+ 'block_device': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BlockDevice']"}),
455+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
456+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
457+ 'table_type': ('django.db.models.fields.CharField', [], {'default': "u'GPT'", 'max_length': '20'}),
458+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
459+ },
460+ u'maasserver.physicalblockdevice': {
461+ 'Meta': {'ordering': "[u'id']", 'object_name': 'PhysicalBlockDevice', '_ormbases': [u'maasserver.BlockDevice']},
462+ u'blockdevice_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['maasserver.BlockDevice']", 'unique': 'True', 'primary_key': 'True'}),
463+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
464+ 'serial': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
465+ },
466+ u'maasserver.space': {
467+ 'Meta': {'object_name': 'Space'},
468+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
469+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
470+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
471+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
472+ },
473+ u'maasserver.sshkey': {
474+ 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'},
475+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
476+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
477+ 'key': ('django.db.models.fields.TextField', [], {}),
478+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
479+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
480+ },
481+ u'maasserver.sslkey': {
482+ 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSLKey'},
483+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
484+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
485+ 'key': ('django.db.models.fields.TextField', [], {}),
486+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
487+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
488+ },
489+ u'maasserver.staticipaddress': {
490+ 'Meta': {'object_name': 'StaticIPAddress'},
491+ 'alloc_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
492+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
493+ 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
494+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
495+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
496+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
497+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
498+ },
499+ u'maasserver.subnet': {
500+ 'Meta': {'unique_together': "((u'name', u'space'),)", 'object_name': 'Subnet'},
501+ 'cidr': ('maasserver.fields.CIDRField', [], {'unique': 'True'}),
502+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
503+ 'dns_servers': ('djorm_pgarray.fields.ArrayField', [], {'default': '[]', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
504+ 'gateway_ip': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
505+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
506+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
507+ 'space': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Space']", 'on_delete': 'models.PROTECT'}),
508+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
509+ 'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.VLAN']", 'on_delete': 'models.PROTECT'})
510+ },
511+ u'maasserver.tag': {
512+ 'Meta': {'object_name': 'Tag'},
513+ 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
514+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
515+ 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
516+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
517+ 'kernel_opts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
518+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
519+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
520+ },
521+ u'maasserver.userprofile': {
522+ 'Meta': {'object_name': 'UserProfile'},
523+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
524+ 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'})
525+ },
526+ u'maasserver.virtualblockdevice': {
527+ 'Meta': {'ordering': "[u'id']", 'object_name': 'VirtualBlockDevice', '_ormbases': [u'maasserver.BlockDevice']},
528+ u'blockdevice_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['maasserver.BlockDevice']", 'unique': 'True', 'primary_key': 'True'}),
529+ 'filesystem_group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'virtual_devices'", 'to': u"orm['maasserver.FilesystemGroup']"}),
530+ 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
531+ },
532+ u'maasserver.vlan': {
533+ 'Meta': {'unique_together': "((u'vid', u'fabric'), (u'name', u'fabric'))", 'object_name': 'VLAN'},
534+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
535+ 'fabric': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Fabric']"}),
536+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
537+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
538+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
539+ 'vid': ('django.db.models.fields.IntegerField', [], {})
540+ },
541+ u'maasserver.zone': {
542+ 'Meta': {'ordering': "[u'name']", 'object_name': 'Zone'},
543+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
544+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
545+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
546+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
547+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
548+ },
549+ u'piston.consumer': {
550+ 'Meta': {'object_name': 'Consumer'},
551+ 'description': ('django.db.models.fields.TextField', [], {}),
552+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
553+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
554+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
555+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
556+ 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}),
557+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': u"orm['auth.User']"})
558+ },
559+ u'piston.token': {
560+ 'Meta': {'object_name': 'Token'},
561+ 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
562+ 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
563+ 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Consumer']"}),
564+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
565+ 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
566+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
567+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
568+ 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1434991806L'}),
569+ 'token_type': ('django.db.models.fields.IntegerField', [], {}),
570+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': u"orm['auth.User']"}),
571+ 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'})
572+ }
573+ }
574+
575+ complete_apps = ['maasserver']
576\ No newline at end of file
577
578=== added file 'src/maasserver/migrations/0143_create_default_space.py'
579--- src/maasserver/migrations/0143_create_default_space.py 1970-01-01 00:00:00 +0000
580+++ src/maasserver/migrations/0143_create_default_space.py 2015-06-22 16:52:47 +0000
581@@ -0,0 +1,501 @@
582+from django.db import models
583+from south.db import db
584+# -*- coding: utf-8 -*-
585+from south.utils import datetime_utils as datetime
586+from south.v2 import DataMigration
587+
588+
589+class Migration(DataMigration):
590+
591+ def forwards(self, orm):
592+ now = datetime.datetime.now()
593+ orm['maasserver.Space'](name="Default space", id=0, created=now, updated=now).save()
594+
595+ def backwards(self, orm):
596+ pass
597+
598+ models = {
599+ u'auth.group': {
600+ 'Meta': {'object_name': 'Group'},
601+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
602+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
603+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
604+ },
605+ u'auth.permission': {
606+ 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
607+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
608+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
609+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
610+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
611+ },
612+ u'auth.user': {
613+ 'Meta': {'object_name': 'User'},
614+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
615+ 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}),
616+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
617+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
618+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
619+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
620+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
621+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
622+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
623+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
624+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
625+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
626+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
627+ },
628+ u'contenttypes.contenttype': {
629+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
630+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
631+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
632+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
633+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
634+ },
635+ u'maasserver.blockdevice': {
636+ 'Meta': {'ordering': "[u'id']", 'unique_together': "((u'node', u'path'),)", 'object_name': 'BlockDevice'},
637+ 'block_size': ('django.db.models.fields.IntegerField', [], {}),
638+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
639+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
640+ 'id_path': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
641+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
642+ 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}),
643+ 'path': ('django.db.models.fields.FilePathField', [], {'max_length': '100'}),
644+ 'size': ('django.db.models.fields.BigIntegerField', [], {}),
645+ 'tags': ('djorm_pgarray.fields.ArrayField', [], {'default': '[]', 'dbtype': "u'text'", 'blank': 'True'}),
646+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
647+ },
648+ u'maasserver.bootresource': {
649+ 'Meta': {'unique_together': "((u'name', u'architecture'),)", 'object_name': 'BootResource'},
650+ 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
651+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
652+ 'extra': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
653+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
654+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
655+ 'rtype': ('django.db.models.fields.IntegerField', [], {'max_length': '10'}),
656+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
657+ },
658+ u'maasserver.bootresourcefile': {
659+ 'Meta': {'unique_together': "((u'resource_set', u'filetype'),)", 'object_name': 'BootResourceFile'},
660+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
661+ 'extra': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
662+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
663+ 'filetype': ('django.db.models.fields.CharField', [], {'default': "u'root-tgz'", 'max_length': '20'}),
664+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
665+ 'largefile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.LargeFile']"}),
666+ 'resource_set': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'files'", 'to': u"orm['maasserver.BootResourceSet']"}),
667+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
668+ },
669+ u'maasserver.bootresourceset': {
670+ 'Meta': {'unique_together': "((u'resource', u'version'),)", 'object_name': 'BootResourceSet'},
671+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
672+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
673+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
674+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'sets'", 'to': u"orm['maasserver.BootResource']"}),
675+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
676+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '255'})
677+ },
678+ u'maasserver.bootsource': {
679+ 'Meta': {'object_name': 'BootSource'},
680+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
681+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
682+ 'keyring_data': ('maasserver.fields.EditableBinaryField', [], {'blank': 'True'}),
683+ 'keyring_filename': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
684+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
685+ 'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200'})
686+ },
687+ u'maasserver.bootsourcecache': {
688+ 'Meta': {'object_name': 'BootSourceCache'},
689+ 'arch': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
690+ 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}),
691+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
692+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
693+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
694+ 'os': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
695+ 'release': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
696+ 'subarch': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
697+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
698+ },
699+ u'maasserver.bootsourceselection': {
700+ 'Meta': {'unique_together': "((u'boot_source', u'os', u'release'),)", 'object_name': 'BootSourceSelection'},
701+ 'arches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
702+ 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}),
703+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
704+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
705+ 'labels': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
706+ 'os': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
707+ 'release': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
708+ 'subarches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
709+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
710+ },
711+ u'maasserver.candidatename': {
712+ 'Meta': {'unique_together': "((u'name', u'position'),)", 'object_name': 'CandidateName'},
713+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
714+ 'name': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
715+ 'position': ('django.db.models.fields.IntegerField', [], {})
716+ },
717+ u'maasserver.componenterror': {
718+ 'Meta': {'object_name': 'ComponentError'},
719+ 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}),
720+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
721+ 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
722+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
723+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
724+ },
725+ u'maasserver.config': {
726+ 'Meta': {'object_name': 'Config'},
727+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
728+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
729+ 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'})
730+ },
731+ u'maasserver.dhcplease': {
732+ 'Meta': {'object_name': 'DHCPLease'},
733+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
734+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
735+ 'mac': ('maasserver.fields.MACAddressField', [], {}),
736+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"})
737+ },
738+ u'maasserver.downloadprogress': {
739+ 'Meta': {'object_name': 'DownloadProgress'},
740+ 'bytes_downloaded': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
741+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
742+ 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}),
743+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
744+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
745+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
746+ 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
747+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
748+ },
749+ u'maasserver.event': {
750+ 'Meta': {'object_name': 'Event'},
751+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
752+ 'description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}),
753+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
754+ 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}),
755+ 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.EventType']"}),
756+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
757+ },
758+ u'maasserver.eventtype': {
759+ 'Meta': {'object_name': 'EventType'},
760+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
761+ 'description': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
762+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
763+ 'level': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
764+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
765+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
766+ },
767+ u'maasserver.fabric': {
768+ 'Meta': {'object_name': 'Fabric'},
769+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
770+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
771+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
772+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
773+ },
774+ u'maasserver.filestorage': {
775+ 'Meta': {'unique_together': "((u'filename', u'owner'),)", 'object_name': 'FileStorage'},
776+ 'content': ('metadataserver.fields.BinaryField', [], {'blank': 'True'}),
777+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
778+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
779+ 'key': ('django.db.models.fields.CharField', [], {'default': "u'e274352a-18fe-11e5-af4a-3c970e0e56dc'", 'unique': 'True', 'max_length': '36'}),
780+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
781+ },
782+ u'maasserver.filesystem': {
783+ 'Meta': {'object_name': 'Filesystem'},
784+ 'block_device': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BlockDevice']", 'null': 'True', 'blank': 'True'}),
785+ 'create_params': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
786+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
787+ 'filesystem_group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'filesystems'", 'null': 'True', 'to': u"orm['maasserver.FilesystemGroup']"}),
788+ 'fstype': ('django.db.models.fields.CharField', [], {'default': "u'ext4'", 'max_length': '20'}),
789+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
790+ 'mount_params': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
791+ 'mount_point': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
792+ 'partition': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Partition']", 'null': 'True', 'blank': 'True'}),
793+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
794+ 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
795+ },
796+ u'maasserver.filesystemgroup': {
797+ 'Meta': {'object_name': 'FilesystemGroup'},
798+ 'create_params': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
799+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
800+ 'group_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
801+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
802+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
803+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
804+ 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
805+ },
806+ u'maasserver.interface': {
807+ 'Meta': {'ordering': "(u'created',)", 'object_name': 'Interface'},
808+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
809+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
810+ 'ipv4_params': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
811+ 'ipv6_params': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
812+ 'mac': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.MACAddress']", 'null': 'True', 'blank': 'True'}),
813+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
814+ 'params': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
815+ 'parents': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['maasserver.Interface']", 'null': 'True', 'through': u"orm['maasserver.InterfaceRelationship']", 'blank': 'True'}),
816+ 'tags': ('djorm_pgarray.fields.ArrayField', [], {'default': '[]', 'dbtype': "u'text'", 'blank': 'True'}),
817+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
818+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
819+ 'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.VLAN']", 'on_delete': 'models.PROTECT'})
820+ },
821+ u'maasserver.interfacerelationship': {
822+ 'Meta': {'object_name': 'InterfaceRelationship'},
823+ 'child': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'parent_relationships'", 'to': u"orm['maasserver.Interface']"}),
824+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
825+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
826+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'children_relationships'", 'to': u"orm['maasserver.Interface']"}),
827+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
828+ },
829+ u'maasserver.largefile': {
830+ 'Meta': {'object_name': 'LargeFile'},
831+ 'content': ('maasserver.fields.LargeObjectField', [], {}),
832+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
833+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
834+ 'sha256': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}),
835+ 'total_size': ('django.db.models.fields.BigIntegerField', [], {}),
836+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
837+ },
838+ u'maasserver.licensekey': {
839+ 'Meta': {'unique_together': "((u'osystem', u'distro_series'),)", 'object_name': 'LicenseKey'},
840+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
841+ 'distro_series': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
842+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
843+ 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
844+ 'osystem': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
845+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
846+ },
847+ u'maasserver.macaddress': {
848+ 'Meta': {'ordering': "(u'created',)", 'object_name': 'MACAddress'},
849+ 'cluster_interface': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['maasserver.NodeGroupInterface']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
850+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
851+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
852+ 'ip_addresses': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.StaticIPAddress']", 'symmetrical': 'False', 'through': u"orm['maasserver.MACStaticIPAddressLink']", 'blank': 'True'}),
853+ 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}),
854+ 'networks': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Network']", 'symmetrical': 'False', 'blank': 'True'}),
855+ 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']", 'null': 'True', 'blank': 'True'}),
856+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
857+ },
858+ u'maasserver.macstaticipaddresslink': {
859+ 'Meta': {'unique_together': "((u'ip_address', u'mac_address'),)", 'object_name': 'MACStaticIPAddressLink'},
860+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
861+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
862+ 'ip_address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.StaticIPAddress']", 'unique': 'True'}),
863+ 'mac_address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.MACAddress']"}),
864+ 'nic_alias': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
865+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
866+ },
867+ u'maasserver.network': {
868+ 'Meta': {'object_name': 'Network'},
869+ 'default_gateway': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
870+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
871+ 'dns_servers': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
872+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
873+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
874+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
875+ 'netmask': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39'}),
876+ 'vlan_tag': ('django.db.models.fields.PositiveSmallIntegerField', [], {'unique': 'True', 'null': 'True', 'blank': 'True'})
877+ },
878+ u'maasserver.node': {
879+ 'Meta': {'object_name': 'Node'},
880+ 'agent_name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
881+ 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '31', 'null': 'True', 'blank': 'True'}),
882+ 'boot_type': ('django.db.models.fields.CharField', [], {'default': "u'fastpath'", 'max_length': '20'}),
883+ 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
884+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
885+ 'disable_ipv4': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
886+ 'distro_series': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
887+ 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
888+ 'error_description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}),
889+ 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}),
890+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
891+ 'installable': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
892+ 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}),
893+ 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
894+ 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
895+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}),
896+ 'osystem': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
897+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
898+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "u'children'", 'null': 'True', 'blank': 'True', 'to': u"orm['maasserver.Node']"}),
899+ 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
900+ 'power_state': ('django.db.models.fields.CharField', [], {'default': "u'unknown'", 'max_length': '10'}),
901+ 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}),
902+ 'pxe_mac': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': u"orm['maasserver.MACAddress']", 'blank': 'True', 'null': 'True'}),
903+ 'routers': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'macaddr'", 'null': 'True', 'blank': 'True'}),
904+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}),
905+ 'swap_size': ('django.db.models.fields.BigIntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
906+ 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-e275dee8-18fe-11e5-af4a-3c970e0e56dc'", 'unique': 'True', 'max_length': '41'}),
907+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}),
908+ 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'null': 'True'}),
909+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
910+ 'zone': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Zone']", 'on_delete': 'models.SET_DEFAULT'})
911+ },
912+ u'maasserver.nodegroup': {
913+ 'Meta': {'object_name': 'NodeGroup'},
914+ 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}),
915+ 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'unique': 'True'}),
916+ 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}),
917+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
918+ 'default_disable_ipv4': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
919+ 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
920+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
921+ 'maas_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
922+ 'name': ('maasserver.models.nodegroup.DomainNameField', [], {'max_length': '80', 'blank': 'True'}),
923+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
924+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
925+ 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
926+ },
927+ u'maasserver.nodegroupinterface': {
928+ 'Meta': {'unique_together': "((u'nodegroup', u'name'),)", 'object_name': 'NodeGroupInterface'},
929+ 'broadcast_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
930+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
931+ 'foreign_dhcp_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
932+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
933+ 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
934+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39'}),
935+ 'ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
936+ 'ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
937+ 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
938+ 'name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
939+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
940+ 'router_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
941+ 'static_ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
942+ 'static_ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
943+ 'subnet_mask': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
944+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
945+ 'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.VLAN']", 'on_delete': 'models.PROTECT'})
946+ },
947+ u'maasserver.partition': {
948+ 'Meta': {'object_name': 'Partition'},
949+ 'bootable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
950+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
951+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
952+ 'partition_table': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'partitions'", 'to': u"orm['maasserver.PartitionTable']"}),
953+ 'size': ('django.db.models.fields.BigIntegerField', [], {}),
954+ 'start_offset': ('django.db.models.fields.BigIntegerField', [], {}),
955+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
956+ 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '36', 'unique': 'True', 'null': 'True', 'blank': 'True'})
957+ },
958+ u'maasserver.partitiontable': {
959+ 'Meta': {'object_name': 'PartitionTable'},
960+ 'block_device': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BlockDevice']"}),
961+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
962+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
963+ 'table_type': ('django.db.models.fields.CharField', [], {'default': "u'GPT'", 'max_length': '20'}),
964+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
965+ },
966+ u'maasserver.physicalblockdevice': {
967+ 'Meta': {'ordering': "[u'id']", 'object_name': 'PhysicalBlockDevice', '_ormbases': [u'maasserver.BlockDevice']},
968+ u'blockdevice_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['maasserver.BlockDevice']", 'unique': 'True', 'primary_key': 'True'}),
969+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
970+ 'serial': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
971+ },
972+ u'maasserver.space': {
973+ 'Meta': {'object_name': 'Space'},
974+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
975+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
976+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
977+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
978+ },
979+ u'maasserver.sshkey': {
980+ 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'},
981+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
982+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
983+ 'key': ('django.db.models.fields.TextField', [], {}),
984+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
985+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
986+ },
987+ u'maasserver.sslkey': {
988+ 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSLKey'},
989+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
990+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
991+ 'key': ('django.db.models.fields.TextField', [], {}),
992+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
993+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
994+ },
995+ u'maasserver.staticipaddress': {
996+ 'Meta': {'object_name': 'StaticIPAddress'},
997+ 'alloc_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
998+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
999+ 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
1000+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
1001+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
1002+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
1003+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
1004+ },
1005+ u'maasserver.subnet': {
1006+ 'Meta': {'unique_together': "((u'name', u'space'),)", 'object_name': 'Subnet'},
1007+ 'cidr': ('maasserver.fields.CIDRField', [], {'unique': 'True'}),
1008+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
1009+ 'dns_servers': ('djorm_pgarray.fields.ArrayField', [], {'default': '[]', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
1010+ 'gateway_ip': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
1011+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
1012+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
1013+ 'space': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Space']", 'on_delete': 'models.PROTECT'}),
1014+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
1015+ 'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.VLAN']", 'on_delete': 'models.PROTECT'})
1016+ },
1017+ u'maasserver.tag': {
1018+ 'Meta': {'object_name': 'Tag'},
1019+ 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
1020+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
1021+ 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
1022+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
1023+ 'kernel_opts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
1024+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
1025+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
1026+ },
1027+ u'maasserver.userprofile': {
1028+ 'Meta': {'object_name': 'UserProfile'},
1029+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
1030+ 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'})
1031+ },
1032+ u'maasserver.virtualblockdevice': {
1033+ 'Meta': {'ordering': "[u'id']", 'object_name': 'VirtualBlockDevice', '_ormbases': [u'maasserver.BlockDevice']},
1034+ u'blockdevice_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['maasserver.BlockDevice']", 'unique': 'True', 'primary_key': 'True'}),
1035+ 'filesystem_group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'virtual_devices'", 'to': u"orm['maasserver.FilesystemGroup']"}),
1036+ 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
1037+ },
1038+ u'maasserver.vlan': {
1039+ 'Meta': {'unique_together': "((u'vid', u'fabric'), (u'name', u'fabric'))", 'object_name': 'VLAN'},
1040+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
1041+ 'fabric': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Fabric']"}),
1042+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
1043+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
1044+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
1045+ 'vid': ('django.db.models.fields.IntegerField', [], {})
1046+ },
1047+ u'maasserver.zone': {
1048+ 'Meta': {'ordering': "[u'name']", 'object_name': 'Zone'},
1049+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
1050+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
1051+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
1052+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
1053+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
1054+ },
1055+ u'piston.consumer': {
1056+ 'Meta': {'object_name': 'Consumer'},
1057+ 'description': ('django.db.models.fields.TextField', [], {}),
1058+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
1059+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
1060+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
1061+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
1062+ 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}),
1063+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': u"orm['auth.User']"})
1064+ },
1065+ u'piston.token': {
1066+ 'Meta': {'object_name': 'Token'},
1067+ 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
1068+ 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
1069+ 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Consumer']"}),
1070+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
1071+ 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
1072+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
1073+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
1074+ 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1434991870L'}),
1075+ 'token_type': ('django.db.models.fields.IntegerField', [], {}),
1076+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': u"orm['auth.User']"}),
1077+ 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'})
1078+ }
1079+ }
1080+
1081+ complete_apps = ['maasserver']
1082+ symmetrical = True
1083
1084=== modified file 'src/maasserver/models/__init__.py'
1085--- src/maasserver/models/__init__.py 2015-06-15 12:06:36 +0000
1086+++ src/maasserver/models/__init__.py 2015-06-22 16:52:47 +0000
1087@@ -41,8 +41,10 @@
1088 'Partition',
1089 'PartitionTable',
1090 'PhysicalBlockDevice',
1091+ 'Space',
1092 'SSHKey',
1093 'SSLKey',
1094+ 'Subnet',
1095 'Tag',
1096 'UserProfile',
1097 'VirtualBlockDevice',
1098@@ -92,9 +94,11 @@
1099 from maasserver.models.partition import Partition
1100 from maasserver.models.partitiontable import PartitionTable
1101 from maasserver.models.physicalblockdevice import PhysicalBlockDevice
1102+from maasserver.models.space import Space
1103 from maasserver.models.sshkey import SSHKey
1104 from maasserver.models.sslkey import SSLKey
1105 from maasserver.models.staticipaddress import StaticIPAddress
1106+from maasserver.models.subnet import Subnet
1107 from maasserver.models.tag import Tag
1108 from maasserver.models.user import create_user
1109 from maasserver.models.userprofile import UserProfile
1110
1111=== added file 'src/maasserver/models/space.py'
1112--- src/maasserver/models/space.py 1970-01-01 00:00:00 +0000
1113+++ src/maasserver/models/space.py 2015-06-22 16:52:47 +0000
1114@@ -0,0 +1,80 @@
1115+# Copyright 2015 Canonical Ltd. This software is licensed under the
1116+# GNU Affero General Public License version 3 (see the file LICENSE).
1117+
1118+"""Space objects."""
1119+
1120+from __future__ import (
1121+ absolute_import,
1122+ print_function,
1123+ unicode_literals,
1124+ )
1125+
1126+str = None
1127+
1128+__metaclass__ = type
1129+__all__ = [
1130+ "DEFAULT_SPACE_NAME",
1131+ "Space",
1132+ "SPACE_NAME_VALIDATOR",
1133+ ]
1134+
1135+import datetime
1136+
1137+from django.core.validators import RegexValidator
1138+from django.db.models import (
1139+ CharField,
1140+ Manager,
1141+)
1142+from maasserver import DefaultMeta
1143+from maasserver.models.cleansave import CleanSave
1144+from maasserver.models.timestampedmodel import TimestampedModel
1145+
1146+
1147+SPACE_NAME_VALIDATOR = RegexValidator('^[ \w-]+$')
1148+
1149+# Name of the special, default space. This space cannot be deleted.
1150+DEFAULT_SPACE_NAME = 'Default space'
1151+
1152+
1153+class SpaceManager(Manager):
1154+ """Manager for :class:`Space` model."""
1155+
1156+ def get_default_space(self):
1157+ """Return the default space."""
1158+ now = datetime.datetime.now()
1159+ space, _ = self.get_or_create(
1160+ id=0,
1161+ defaults={
1162+ 'id': 0,
1163+ 'name': DEFAULT_SPACE_NAME,
1164+ 'created': now,
1165+ 'updated': now,
1166+ }
1167+ )
1168+ return space
1169+
1170+
1171+class Space(CleanSave, TimestampedModel):
1172+ """A `Space`.
1173+
1174+ :ivar name: The short-human-identifiable name for this space.
1175+ :ivar objects: An instance of the class :class:`SpaceManager`.
1176+ """
1177+
1178+ class Meta(DefaultMeta):
1179+ """Needed for South to recognize this model."""
1180+ verbose_name = "Space"
1181+ verbose_name_plural = "Spaces"
1182+
1183+ objects = SpaceManager()
1184+
1185+ name = CharField(
1186+ max_length=256, unique=True, editable=True,
1187+ validators=[SPACE_NAME_VALIDATOR])
1188+
1189+ def __unicode__(self):
1190+ return "name=%s" % self.name
1191+
1192+ def is_default(self):
1193+ """Is this the default space?"""
1194+ return self.id == 0
1195
1196=== added file 'src/maasserver/models/subnet.py'
1197--- src/maasserver/models/subnet.py 1970-01-01 00:00:00 +0000
1198+++ src/maasserver/models/subnet.py 2015-06-22 16:52:47 +0000
1199@@ -0,0 +1,94 @@
1200+# Copyright 2015 Canonical Ltd. This software is licensed under the
1201+# GNU Affero General Public License version 3 (see the file LICENSE).
1202+
1203+"""Model for subnets."""
1204+
1205+from __future__ import (
1206+ absolute_import,
1207+ print_function,
1208+ unicode_literals,
1209+)
1210+
1211+str = None
1212+
1213+__metaclass__ = type
1214+__all__ = [
1215+ 'Subnet',
1216+]
1217+
1218+
1219+from django.core.exceptions import ValidationError
1220+from django.core.validators import RegexValidator
1221+from django.db.models import (
1222+ CharField,
1223+ ForeignKey,
1224+ PROTECT,
1225+)
1226+from djorm_pgarray.fields import ArrayField
1227+from maasserver import DefaultMeta
1228+from maasserver.fields import (
1229+ CIDRField,
1230+ MAASIPAddressField,
1231+)
1232+from maasserver.models.cleansave import CleanSave
1233+from maasserver.models.timestampedmodel import TimestampedModel
1234+from netaddr import (
1235+ IPAddress,
1236+ IPNetwork,
1237+)
1238+
1239+
1240+SUBNET_NAME_VALIDATOR = RegexValidator('^[.: \w/-]+$')
1241+
1242+
1243+def get_default_vlan():
1244+ from maasserver.models.vlan import VLAN
1245+ return VLAN.objects.get_default_vlan()
1246+
1247+
1248+class Subnet(CleanSave, TimestampedModel):
1249+
1250+ class Meta(DefaultMeta):
1251+ """Needed for South to recognize this model."""
1252+ unique_together = (
1253+ ('name', 'space'),
1254+ )
1255+
1256+ name = CharField(
1257+ blank=False, editable=True, max_length=255,
1258+ validators=[SUBNET_NAME_VALIDATOR],
1259+ help_text="Identifying name for this subnet.")
1260+
1261+ vlan = ForeignKey(
1262+ 'VLAN', default=get_default_vlan, editable=True, blank=False,
1263+ null=False, on_delete=PROTECT)
1264+
1265+ space = ForeignKey(
1266+ 'Space', editable=True, blank=False, null=False, on_delete=PROTECT)
1267+
1268+ # XXX:fabric: unique constraint should be relaxed once proper support for
1269+ # fabrics is implemented. The CIDR must be unique withing a Fabric, not
1270+ # globally unique.
1271+ cidr = CIDRField(
1272+ blank=False, unique=True, editable=True, null=False)
1273+
1274+ gateway_ip = MAASIPAddressField(blank=True, editable=True, null=True)
1275+
1276+ dns_servers = ArrayField(
1277+ dbtype="text", blank=True, editable=True, null=True, default=[])
1278+
1279+ def get_cidr(self):
1280+ return IPNetwork(self.cidr)
1281+
1282+ def __unicode__(self):
1283+ return "name=%s vlan.vid=%s, cidr=%s" % (
1284+ self.name, self.vlan.vid, self.cidr)
1285+
1286+ def validate_gateway_ip(self):
1287+ gateway_addr = IPAddress(self.gateway_ip)
1288+ if gateway_addr not in self.get_cidr():
1289+ message = "Gateway IP must be within CIDR range."
1290+ raise ValidationError({'gateway_ip': [message]})
1291+
1292+ def clean(self, *args, **kwargs):
1293+ self.validate_gateway_ip()
1294
1295=== added file 'src/maasserver/models/tests/test_space.py'
1296--- src/maasserver/models/tests/test_space.py 1970-01-01 00:00:00 +0000
1297+++ src/maasserver/models/tests/test_space.py 2015-06-22 16:52:47 +0000
1298@@ -0,0 +1,69 @@
1299+
1300+# Copyright 2015 Canonical Ltd. This software is licensed under the
1301+# GNU Affero General Public License version 3 (see the file LICENSE).
1302+
1303+"""Tests for the Space model."""
1304+
1305+from __future__ import (
1306+ absolute_import,
1307+ print_function,
1308+ unicode_literals,
1309+ )
1310+
1311+str = None
1312+
1313+__metaclass__ = type
1314+__all__ = []
1315+
1316+
1317+from django.db.models import ProtectedError
1318+from maasserver.models.space import (
1319+ DEFAULT_SPACE_NAME,
1320+ Space,
1321+)
1322+from maasserver.testing.factory import factory
1323+from maasserver.testing.testcase import MAASServerTestCase
1324+from testtools.matchers import MatchesStructure
1325+from testtools.testcase import ExpectedException
1326+
1327+
1328+class SpaceTest(MAASServerTestCase):
1329+
1330+ def test_creates_space(self):
1331+ name = factory.make_name('name')
1332+ space = Space(name=name)
1333+ space.save()
1334+ space_from_db = Space.objects.get(name=name)
1335+ self.assertThat(space_from_db, MatchesStructure.byEquality(
1336+ name=name))
1337+
1338+ def test_get_default_space_creates_default_space(self):
1339+ default_space = Space.objects.get_default_space()
1340+ self.assertThat(default_space, MatchesStructure.byEquality(
1341+ id=0, name=DEFAULT_SPACE_NAME))
1342+
1343+ def test_get_default_space_is_idempotent(self):
1344+ default_space = Space.objects.get_default_space()
1345+ default_space2 = Space.objects.get_default_space()
1346+ self.assertEqual(default_space.id, default_space2.id)
1347+
1348+ def test_is_default_detects_default_space(self):
1349+ default_space = Space.objects.get_default_space()
1350+ self.assertTrue(default_space.is_default())
1351+
1352+ def test_is_default_detects_non_default_space(self):
1353+ name = factory.make_name('name')
1354+ space = factory.make_Space(name=name)
1355+ self.assertFalse(space.is_default())
1356+
1357+ def test_can_be_deleted_if_does_not_contain_subnets(self):
1358+ name = factory.make_name('name')
1359+ space = factory.make_Space(name=name)
1360+ space.delete()
1361+ self.assertItemsEqual([], Space.objects.filter(name=name))
1362+
1363+ def test_cant_be_deleted_if_contains_subnet(self):
1364+ space = factory.make_Space()
1365+ factory.make_Subnet(space=space)
1366+ with ExpectedException(ProtectedError):
1367+ space.delete()
1368
1369=== added file 'src/maasserver/models/tests/test_subnet.py'
1370--- src/maasserver/models/tests/test_subnet.py 1970-01-01 00:00:00 +0000
1371+++ src/maasserver/models/tests/test_subnet.py 2015-06-22 16:52:47 +0000
1372@@ -0,0 +1,54 @@
1373+# Copyright 2015 Canonical Ltd. This software is licensed under the
1374+# GNU Affero General Public License version 3 (see the file LICENSE).
1375+
1376+"""Tests for the Subnet model."""
1377+
1378+from __future__ import (
1379+ absolute_import,
1380+ print_function,
1381+ unicode_literals,
1382+ )
1383+
1384+str = None
1385+
1386+__metaclass__ = type
1387+__all__ = []
1388+
1389+
1390+import random
1391+
1392+from django.core.exceptions import ValidationError
1393+from maasserver.models.subnet import Subnet
1394+from maasserver.testing.factory import factory
1395+from maasserver.testing.testcase import MAASServerTestCase
1396+from testtools.matchers import MatchesStructure
1397+
1398+
1399+class SubnetTest(MAASServerTestCase):
1400+
1401+ def test_creates_subnet(self):
1402+ name = factory.make_name('name')
1403+ vlan = factory.make_VLAN()
1404+ space = factory.make_Space()
1405+ network = factory.make_ip4_or_6_network()
1406+ cidr = unicode(network.cidr)
1407+ gateway_ip = factory.pick_ip_in_network(network)
1408+ dns_servers = [
1409+ factory.make_ip_address()
1410+ for _ in range(random.randint(1, 3))]
1411+ subnet = Subnet(
1412+ name=name, vlan=vlan, cidr=cidr, gateway_ip=gateway_ip,
1413+ space=space, dns_servers=dns_servers)
1414+ subnet.save()
1415+ subnet_from_db = Subnet.objects.get(name=name)
1416+ self.assertThat(subnet_from_db, MatchesStructure.byEquality(
1417+ name=name, vlan=vlan, cidr=cidr, space=space,
1418+ gateway_ip=gateway_ip, dns_servers=dns_servers))
1419+
1420+ def test_validates_gateway_ip(self):
1421+ error = self.assertRaises(
1422+ ValidationError, factory.make_Subnet, cidr='192.168.0.0/24',
1423+ gateway_ip='10.0.0.0')
1424+ self.assertEqual(
1425+ {'gateway_ip': ["Gateway IP must be within CIDR range."]},
1426+ error.message_dict)
1427
1428=== modified file 'src/maasserver/models/tests/test_vlan.py'
1429--- src/maasserver/models/tests/test_vlan.py 2015-06-18 10:24:21 +0000
1430+++ src/maasserver/models/tests/test_vlan.py 2015-06-22 16:52:47 +0000
1431@@ -26,6 +26,7 @@
1432 from maasserver.models.nodegroupinterface import NodeGroupInterface
1433 from maasserver.models.vlan import VLAN
1434 from maasserver.testing.factory import factory
1435+from maasserver.testing.orm import reload_object
1436 from maasserver.testing.testcase import MAASServerTestCase
1437 from testtools.matchers import MatchesStructure
1438 from testtools.testcase import ExpectedException
1439@@ -115,6 +116,14 @@
1440 self.assertEqual(
1441 reconnected_interface.vlan, fabric.get_default_vlan())
1442
1443+ def test_subnets_are_reconnected_when_vlan_is_deleted(self):
1444+ fabric = factory.make_Fabric()
1445+ vlan = factory.make_VLAN(fabric=fabric)
1446+ subnet = factory.make_Subnet(vlan=vlan)
1447+ vlan.delete()
1448+ self.assertEqual(
1449+ reload_object(subnet).vlan, fabric.get_default_vlan())
1450+
1451
1452 class VLANVidValidationTest(MAASServerTestCase):
1453
1454
1455=== modified file 'src/maasserver/models/vlan.py'
1456--- src/maasserver/models/vlan.py 2015-06-18 10:24:21 +0000
1457+++ src/maasserver/models/vlan.py 2015-06-22 16:52:47 +0000
1458@@ -112,6 +112,12 @@
1459 ngi.vlan = self.fabric.get_default_vlan()
1460 ngi.save()
1461
1462+ def manage_connected_subnets(self):
1463+ """Reconnect subnets the default VLAN of the fabric."""
1464+ for subnet in self.subnet_set.all():
1465+ subnet.vlan = self.fabric.get_default_vlan()
1466+ subnet.save()
1467+
1468 def delete(self):
1469 if self.is_fabric_default():
1470 raise ValidationError(
1471@@ -119,4 +125,5 @@
1472 "it cannot be deleted.")
1473 self.manage_connected_interfaces()
1474 self.manage_connected_cluster_interfaces()
1475+ self.manage_connected_subnets()
1476 super(VLAN, self).delete()
1477
1478=== modified file 'src/maasserver/testing/factory.py'
1479--- src/maasserver/testing/factory.py 2015-06-17 13:11:27 +0000
1480+++ src/maasserver/testing/factory.py 2015-06-22 16:52:47 +0000
1481@@ -72,9 +72,11 @@
1482 Partition,
1483 PartitionTable,
1484 PhysicalBlockDevice,
1485+ Space,
1486 SSHKey,
1487 SSLKey,
1488 StaticIPAddress,
1489+ Subnet,
1490 Tag,
1491 VirtualBlockDevice,
1492 VLAN,
1493@@ -102,6 +104,7 @@
1494 )
1495 from netaddr import (
1496 IPAddress,
1497+ IPNetwork,
1498 IPRange,
1499 )
1500 from provisioningserver.utils.enum import map_enum
1501@@ -588,6 +591,35 @@
1502 key.save()
1503 return key
1504
1505+ def make_Space(self, name=None):
1506+ if name is None:
1507+ name = self.make_name('space')
1508+ space = Space(name=name)
1509+ space.save()
1510+ return space
1511+
1512+ def make_Subnet(self, name=None, vlan=None, space=None, cidr=None,
1513+ gateway_ip=None, dns_servers=None):
1514+ if name is None:
1515+ name = factory.make_name('name')
1516+ if vlan is None:
1517+ vlan = factory.make_VLAN()
1518+ if space is None:
1519+ space = factory.make_Space()
1520+ if cidr is None:
1521+ network = factory.make_ip4_or_6_network()
1522+ cidr = unicode(network.cidr)
1523+ if gateway_ip is None:
1524+ gateway_ip = factory.pick_ip_in_network(IPNetwork(cidr))
1525+ if dns_servers is None:
1526+ dns_servers = [
1527+ self.make_ip_address() for _ in range(random.randint(1, 3))]
1528+ subnet = Subnet(
1529+ name=name, vlan=vlan, cidr=cidr, gateway_ip=gateway_ip,
1530+ space=space, dns_servers=dns_servers)
1531+ subnet.save()
1532+ return subnet
1533+
1534 def make_Fabric(self, name=None):
1535 if name is None:
1536 name = self.make_name('fabric')
1537
1538=== modified file 'src/maasserver/tests/test_fields.py'
1539--- src/maasserver/tests/test_fields.py 2015-06-17 14:52:20 +0000
1540+++ src/maasserver/tests/test_fields.py 2015-06-22 16:52:47 +0000
1541@@ -52,7 +52,6 @@
1542 )
1543 from maastesting.djangotestcase import TestModelMixin
1544 from maastesting.matchers import MockCalledOnceWith
1545-from netaddr import IPNetwork
1546 from psycopg2 import OperationalError
1547 from psycopg2.extensions import ISQLQuote
1548
1549@@ -530,7 +529,7 @@
1550 def test_stores_cidr(self):
1551 cidr = '192.0.2.0/24'
1552 instance = CIDRTestModel.objects.create(cidr=cidr)
1553- self.assertEqual(reload_object(instance).cidr, IPNetwork(cidr))
1554+ self.assertEqual(cidr, reload_object(instance).cidr)
1555
1556 def test_validates_cidr(self):
1557 cidr = 'invalid-cidr'
1558@@ -540,5 +539,6 @@
1559
1560 def test_stores_cidr_with_bit_set_in_host_part(self):
1561 cidr = '192.0.2.1/24'
1562+ normalized_cidr = '192.0.2.0/24'
1563 instance = CIDRTestModel.objects.create(cidr=cidr)
1564- self.assertEqual(reload_object(instance).cidr, IPNetwork(cidr))
1565+ self.assertEqual(normalized_cidr, reload_object(instance).cidr)
1566
1567=== modified file 'src/maastesting/factory.py'
1568--- src/maastesting/factory.py 2015-06-10 20:01:19 +0000
1569+++ src/maastesting/factory.py 2015-06-22 16:52:47 +0000
1570@@ -263,6 +263,13 @@
1571 slash=slash, but_not=but_not, disjoint_from=disjoint_from,
1572 random_address_factory=self.make_ipv6_address)
1573
1574+ def make_ip4_or_6_network(self):
1575+ """Generate a random IPv4 or IPv6 network."""
1576+ if random.randint(0, 1) == 0:
1577+ return self.make_ipv4_network()
1578+ else:
1579+ return self.make_ipv6_network()
1580+
1581 def pick_ip_in_network(self, network, but_not=None):
1582 if but_not is None:
1583 but_not = []