Merge lp:~blake-rouse/maas/partition-model into lp:~maas-committers/maas/trunk

Proposed by Blake Rouse
Status: Merged
Approved by: Blake Rouse
Approved revision: no longer in the source branch.
Merged at revision: 3638
Proposed branch: lp:~blake-rouse/maas/partition-model
Merge into: lp:~maas-committers/maas/trunk
Prerequisite: lp:~blake-rouse/maas/partition-table-model
Diff against target: 632 lines (+573/-1)
5 files modified
src/maasserver/migrations/0130_add_partition_model.py (+422/-0)
src/maasserver/models/__init__.py (+3/-1)
src/maasserver/models/partition.py (+76/-0)
src/maasserver/models/tests/test_partition.py (+56/-0)
src/maasserver/testing/factory.py (+16/-0)
To merge this branch: bzr merge lp:~blake-rouse/maas/partition-model
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+252481@code.launchpad.net

Commit message

Add new partition model.

Adds the data model that will be used to configure the partitions that we be placed on the disk when a node is deployed.

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

Looks good, but there are a few things that need to be fixed.

review: Needs Fixing
Revision history for this message
Gavin Panella (allenap) wrote :

The fixes look good :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'src/maasserver/migrations/0130_add_partition_model.py'
2--- src/maasserver/migrations/0130_add_partition_model.py 1970-01-01 00:00:00 +0000
3+++ src/maasserver/migrations/0130_add_partition_model.py 2015-03-12 03:23:40 +0000
4@@ -0,0 +1,422 @@
5+from django.db import models
6+from south.db import db
7+# -*- coding: utf-8 -*-
8+from south.utils import datetime_utils as datetime
9+from south.v2 import SchemaMigration
10+
11+
12+class Migration(SchemaMigration):
13+
14+ def forwards(self, orm):
15+ # Adding model 'Partition'
16+ db.create_table(u'maasserver_partition', (
17+ (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
18+ ('created', self.gf('django.db.models.fields.DateTimeField')()),
19+ ('updated', self.gf('django.db.models.fields.DateTimeField')()),
20+ ('partition_table', self.gf('django.db.models.fields.related.ForeignKey')(related_name=u'partitions', to=orm['maasserver.PartitionTable'])),
21+ ('uuid', self.gf('django.db.models.fields.CharField')(max_length=36, unique=True, null=True, blank=True)),
22+ ('start_offset', self.gf('django.db.models.fields.BigIntegerField')()),
23+ ('size', self.gf('django.db.models.fields.BigIntegerField')()),
24+ ('bootable', self.gf('django.db.models.fields.BooleanField')(default=False)),
25+ ))
26+ db.send_create_signal(u'maasserver', ['Partition'])
27+
28+
29+ def backwards(self, orm):
30+ # Deleting model 'Partition'
31+ db.delete_table(u'maasserver_partition')
32+
33+
34+ models = {
35+ u'auth.group': {
36+ 'Meta': {'object_name': 'Group'},
37+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
38+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
39+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
40+ },
41+ u'auth.permission': {
42+ 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
43+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
44+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
45+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
46+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
47+ },
48+ u'auth.user': {
49+ 'Meta': {'object_name': 'User'},
50+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
51+ 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}),
52+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
53+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
54+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
55+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
56+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
57+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
58+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
59+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
60+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
61+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
62+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
63+ },
64+ u'contenttypes.contenttype': {
65+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
66+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
67+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
68+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
69+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
70+ },
71+ u'maasserver.blockdevice': {
72+ 'Meta': {'ordering': "[u'id']", 'unique_together': "((u'node', u'path'),)", 'object_name': 'BlockDevice'},
73+ 'block_size': ('django.db.models.fields.IntegerField', [], {}),
74+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
75+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
76+ 'id_path': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
77+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
78+ 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}),
79+ 'path': ('django.db.models.fields.FilePathField', [], {'max_length': '100'}),
80+ 'size': ('django.db.models.fields.BigIntegerField', [], {}),
81+ 'tags': ('djorm_pgarray.fields.ArrayField', [], {'default': '[]', 'dbtype': "u'text'", 'blank': 'True'}),
82+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
83+ },
84+ u'maasserver.bootresource': {
85+ 'Meta': {'unique_together': "((u'name', u'architecture'),)", 'object_name': 'BootResource'},
86+ 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
87+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
88+ 'extra': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
89+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
90+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
91+ 'rtype': ('django.db.models.fields.IntegerField', [], {'max_length': '10'}),
92+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
93+ },
94+ u'maasserver.bootresourcefile': {
95+ 'Meta': {'unique_together': "((u'resource_set', u'filetype'),)", 'object_name': 'BootResourceFile'},
96+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
97+ 'extra': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
98+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
99+ 'filetype': ('django.db.models.fields.CharField', [], {'default': "u'root-tgz'", 'max_length': '20'}),
100+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
101+ 'largefile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.LargeFile']"}),
102+ 'resource_set': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'files'", 'to': u"orm['maasserver.BootResourceSet']"}),
103+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
104+ },
105+ u'maasserver.bootresourceset': {
106+ 'Meta': {'unique_together': "((u'resource', u'version'),)", 'object_name': 'BootResourceSet'},
107+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
108+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
109+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
110+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'sets'", 'to': u"orm['maasserver.BootResource']"}),
111+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
112+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '255'})
113+ },
114+ u'maasserver.bootsource': {
115+ 'Meta': {'object_name': 'BootSource'},
116+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
117+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
118+ 'keyring_data': ('maasserver.fields.EditableBinaryField', [], {'blank': 'True'}),
119+ 'keyring_filename': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
120+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
121+ 'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200'})
122+ },
123+ u'maasserver.bootsourcecache': {
124+ 'Meta': {'object_name': 'BootSourceCache'},
125+ 'arch': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
126+ 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}),
127+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
128+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
129+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
130+ 'os': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
131+ 'release': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
132+ 'subarch': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
133+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
134+ },
135+ u'maasserver.bootsourceselection': {
136+ 'Meta': {'unique_together': "((u'boot_source', u'os', u'release'),)", 'object_name': 'BootSourceSelection'},
137+ 'arches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
138+ 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}),
139+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
140+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
141+ 'labels': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
142+ 'os': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
143+ 'release': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
144+ 'subarches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
145+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
146+ },
147+ u'maasserver.candidatename': {
148+ 'Meta': {'unique_together': "((u'name', u'position'),)", 'object_name': 'CandidateName'},
149+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
150+ 'name': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
151+ 'position': ('django.db.models.fields.IntegerField', [], {})
152+ },
153+ u'maasserver.componenterror': {
154+ 'Meta': {'object_name': 'ComponentError'},
155+ 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}),
156+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
157+ 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
158+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
159+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
160+ },
161+ u'maasserver.config': {
162+ 'Meta': {'object_name': 'Config'},
163+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
164+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
165+ 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'})
166+ },
167+ u'maasserver.dhcplease': {
168+ 'Meta': {'object_name': 'DHCPLease'},
169+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
170+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
171+ 'mac': ('maasserver.fields.MACAddressField', [], {}),
172+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"})
173+ },
174+ u'maasserver.downloadprogress': {
175+ 'Meta': {'object_name': 'DownloadProgress'},
176+ 'bytes_downloaded': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
177+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
178+ 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}),
179+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
180+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
181+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
182+ 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
183+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
184+ },
185+ u'maasserver.event': {
186+ 'Meta': {'object_name': 'Event'},
187+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
188+ 'description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}),
189+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
190+ 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}),
191+ 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.EventType']"}),
192+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
193+ },
194+ u'maasserver.eventtype': {
195+ 'Meta': {'object_name': 'EventType'},
196+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
197+ 'description': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
198+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
199+ 'level': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
200+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
201+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
202+ },
203+ u'maasserver.filestorage': {
204+ 'Meta': {'unique_together': "((u'filename', u'owner'),)", 'object_name': 'FileStorage'},
205+ 'content': ('metadataserver.fields.BinaryField', [], {'blank': 'True'}),
206+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
207+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
208+ 'key': ('django.db.models.fields.CharField', [], {'default': "u'c76af252-c866-11e4-b9cc-bcee7b78dc5b'", 'unique': 'True', 'max_length': '36'}),
209+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
210+ },
211+ u'maasserver.largefile': {
212+ 'Meta': {'object_name': 'LargeFile'},
213+ 'content': ('maasserver.fields.LargeObjectField', [], {}),
214+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
215+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
216+ 'sha256': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}),
217+ 'total_size': ('django.db.models.fields.BigIntegerField', [], {}),
218+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
219+ },
220+ u'maasserver.licensekey': {
221+ 'Meta': {'unique_together': "((u'osystem', u'distro_series'),)", 'object_name': 'LicenseKey'},
222+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
223+ 'distro_series': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
224+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
225+ 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
226+ 'osystem': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
227+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
228+ },
229+ u'maasserver.macaddress': {
230+ 'Meta': {'ordering': "(u'created',)", 'object_name': 'MACAddress'},
231+ 'cluster_interface': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['maasserver.NodeGroupInterface']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
232+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
233+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
234+ 'ip_addresses': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.StaticIPAddress']", 'symmetrical': 'False', 'through': u"orm['maasserver.MACStaticIPAddressLink']", 'blank': 'True'}),
235+ 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}),
236+ 'networks': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Network']", 'symmetrical': 'False', 'blank': 'True'}),
237+ 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']", 'null': 'True', 'blank': 'True'}),
238+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
239+ },
240+ u'maasserver.macstaticipaddresslink': {
241+ 'Meta': {'unique_together': "((u'ip_address', u'mac_address'),)", 'object_name': 'MACStaticIPAddressLink'},
242+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
243+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
244+ 'ip_address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.StaticIPAddress']", 'unique': 'True'}),
245+ 'mac_address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.MACAddress']"}),
246+ 'nic_alias': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
247+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
248+ },
249+ u'maasserver.network': {
250+ 'Meta': {'object_name': 'Network'},
251+ 'default_gateway': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
252+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
253+ 'dns_servers': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
254+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
255+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
256+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
257+ 'netmask': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39'}),
258+ 'vlan_tag': ('django.db.models.fields.PositiveSmallIntegerField', [], {'unique': 'True', 'null': 'True', 'blank': 'True'})
259+ },
260+ u'maasserver.node': {
261+ 'Meta': {'object_name': 'Node'},
262+ 'agent_name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
263+ 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '31', 'null': 'True', 'blank': 'True'}),
264+ 'boot_type': ('django.db.models.fields.CharField', [], {'default': "u'fastpath'", 'max_length': '20'}),
265+ 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
266+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
267+ 'disable_ipv4': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
268+ 'distro_series': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
269+ 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
270+ 'error_description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}),
271+ 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}),
272+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
273+ 'installable': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
274+ 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}),
275+ 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
276+ 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
277+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}),
278+ 'osystem': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
279+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
280+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "u'children'", 'null': 'True', 'blank': 'True', 'to': u"orm['maasserver.Node']"}),
281+ 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
282+ 'power_state': ('django.db.models.fields.CharField', [], {'default': "u'unknown'", 'max_length': '10'}),
283+ 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}),
284+ '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'}),
285+ 'routers': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'macaddr'", 'null': 'True', 'blank': 'True'}),
286+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}),
287+ 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-c76a0bf8-c866-11e4-b9cc-bcee7b78dc5b'", 'unique': 'True', 'max_length': '41'}),
288+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}),
289+ 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'null': 'True'}),
290+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
291+ 'zone': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Zone']", 'on_delete': 'models.SET_DEFAULT'})
292+ },
293+ u'maasserver.nodegroup': {
294+ 'Meta': {'object_name': 'NodeGroup'},
295+ 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}),
296+ 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'unique': 'True'}),
297+ 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}),
298+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
299+ 'default_disable_ipv4': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
300+ 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
301+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
302+ 'maas_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
303+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
304+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
305+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
306+ 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
307+ },
308+ u'maasserver.nodegroupinterface': {
309+ 'Meta': {'unique_together': "((u'nodegroup', u'name'),)", 'object_name': 'NodeGroupInterface'},
310+ 'broadcast_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
311+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
312+ 'foreign_dhcp_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
313+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
314+ 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
315+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39'}),
316+ 'ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
317+ 'ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
318+ 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
319+ 'name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
320+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
321+ 'router_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
322+ 'static_ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
323+ 'static_ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
324+ 'subnet_mask': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
325+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
326+ },
327+ u'maasserver.partition': {
328+ 'Meta': {'object_name': 'Partition'},
329+ 'bootable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
330+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
331+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
332+ 'partition_table': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'partitions'", 'to': u"orm['maasserver.PartitionTable']"}),
333+ 'size': ('django.db.models.fields.BigIntegerField', [], {}),
334+ 'start_offset': ('django.db.models.fields.BigIntegerField', [], {}),
335+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
336+ 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '36', 'unique': 'True', 'null': 'True', 'blank': 'True'})
337+ },
338+ u'maasserver.partitiontable': {
339+ 'Meta': {'object_name': 'PartitionTable'},
340+ 'block_device': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BlockDevice']"}),
341+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
342+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
343+ 'table_type': ('django.db.models.fields.CharField', [], {'default': "u'GPT'", 'max_length': '20'}),
344+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
345+ },
346+ u'maasserver.physicalblockdevice': {
347+ 'Meta': {'ordering': "[u'id']", 'object_name': 'PhysicalBlockDevice', '_ormbases': [u'maasserver.BlockDevice']},
348+ u'blockdevice_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['maasserver.BlockDevice']", 'unique': 'True', 'primary_key': 'True'}),
349+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
350+ 'serial': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
351+ },
352+ u'maasserver.sshkey': {
353+ 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'},
354+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
355+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
356+ 'key': ('django.db.models.fields.TextField', [], {}),
357+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
358+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
359+ },
360+ u'maasserver.sslkey': {
361+ 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSLKey'},
362+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
363+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
364+ 'key': ('django.db.models.fields.TextField', [], {}),
365+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
366+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
367+ },
368+ u'maasserver.staticipaddress': {
369+ 'Meta': {'object_name': 'StaticIPAddress'},
370+ 'alloc_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
371+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
372+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
373+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
374+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
375+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
376+ },
377+ u'maasserver.tag': {
378+ 'Meta': {'object_name': 'Tag'},
379+ 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
380+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
381+ 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
382+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
383+ 'kernel_opts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
384+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
385+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
386+ },
387+ u'maasserver.userprofile': {
388+ 'Meta': {'object_name': 'UserProfile'},
389+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
390+ 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'})
391+ },
392+ u'maasserver.zone': {
393+ 'Meta': {'ordering': "[u'name']", 'object_name': 'Zone'},
394+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
395+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
396+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
397+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
398+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
399+ },
400+ u'piston.consumer': {
401+ 'Meta': {'object_name': 'Consumer'},
402+ 'description': ('django.db.models.fields.TextField', [], {}),
403+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
404+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
405+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
406+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
407+ 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}),
408+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': u"orm['auth.User']"})
409+ },
410+ u'piston.token': {
411+ 'Meta': {'object_name': 'Token'},
412+ 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
413+ 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
414+ 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Consumer']"}),
415+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
416+ 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
417+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
418+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
419+ 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1426130448L'}),
420+ 'token_type': ('django.db.models.fields.IntegerField', [], {}),
421+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': u"orm['auth.User']"}),
422+ 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'})
423+ }
424+ }
425+
426+ complete_apps = ['maasserver']
427\ No newline at end of file
428
429=== modified file 'src/maasserver/models/__init__.py'
430--- src/maasserver/models/__init__.py 2015-03-12 03:23:40 +0000
431+++ src/maasserver/models/__init__.py 2015-03-12 03:23:40 +0000
432@@ -35,6 +35,7 @@
433 'Node',
434 'NodeGroup',
435 'NodeGroupInterface',
436+ 'Partition',
437 'PartitionTable',
438 'PhysicalBlockDevice',
439 'SSHKey',
440@@ -80,6 +81,7 @@
441 )
442 from maasserver.models.nodegroup import NodeGroup
443 from maasserver.models.nodegroupinterface import NodeGroupInterface
444+from maasserver.models.partition import Partition
445 from maasserver.models.partitiontable import PartitionTable
446 from maasserver.models.physicalblockdevice import PhysicalBlockDevice
447 from maasserver.models.sshkey import SSHKey
448@@ -99,7 +101,7 @@
449 ComponentError, Config, DHCPLease, DownloadProgress, Event, EventType,
450 FileStorage, LargeFile, LicenseKey, StaticIPAddress, MACAddress,
451 MACStaticIPAddressLink, Network, NodeGroup, NodeGroupInterface,
452- PartitionTable, SSHKey, Tag, UserProfile, Zone, logger)
453+ Partition, PartitionTable, SSHKey, Tag, UserProfile, Zone, logger)
454
455
456 # Connect the 'create_user' method to the post save signal of User.
457
458=== added file 'src/maasserver/models/partition.py'
459--- src/maasserver/models/partition.py 1970-01-01 00:00:00 +0000
460+++ src/maasserver/models/partition.py 2015-03-12 03:23:40 +0000
461@@ -0,0 +1,76 @@
462+# Copyright 2015 Canonical Ltd. This software is licensed under the
463+# GNU Affero General Public License version 3 (see the file LICENSE).
464+
465+"""Model for a partition in a partition table."""
466+
467+from __future__ import (
468+ absolute_import,
469+ print_function,
470+ unicode_literals,
471+ )
472+
473+str = None
474+
475+__metaclass__ = type
476+__all__ = [
477+ 'Partition',
478+ ]
479+
480+from uuid import uuid4
481+
482+from django.db.models import (
483+ BigIntegerField,
484+ BooleanField,
485+ CharField,
486+ ForeignKey,
487+ )
488+from maasserver import DefaultMeta
489+from maasserver.enum import PARTITION_TABLE_TYPE
490+from maasserver.models.cleansave import CleanSave
491+from maasserver.models.partitiontable import PartitionTable
492+from maasserver.models.timestampedmodel import TimestampedModel
493+
494+
495+class Partition(CleanSave, TimestampedModel):
496+ """A partition in a partition table.
497+
498+ :ivar partition_table: `PartitionTable` this partition belongs to.
499+ :ivar uuid: UUID of the partition if it's part of a GPT partition.
500+ :ivar start_offset: Offset in bytes the partition is from the start of
501+ the disk.
502+ :ivar size: Size of the partition in bytes.
503+ :ivar bootable: Whether the partition is set as bootable.
504+ """
505+
506+ class Meta(DefaultMeta):
507+ """Needed for South to recognize this model."""
508+
509+ partition_table = ForeignKey(
510+ PartitionTable, null=False, blank=False, related_name="partitions")
511+
512+ uuid = CharField(
513+ max_length=36, unique=True, null=True, blank=True)
514+
515+ start_offset = BigIntegerField(null=False)
516+
517+ size = BigIntegerField(null=False)
518+
519+ bootable = BooleanField(default=False)
520+
521+ def get_node(self):
522+ """`Node` this partition belongs to."""
523+ return self.partition_table.get_node()
524+
525+ def get_block_size(self):
526+ """Block size of partition."""
527+ return self.partition_table.get_block_size()
528+
529+ def save(self, *args, **kwargs):
530+ """Save partition."""
531+ if (self.partition_table.table_type == PARTITION_TABLE_TYPE.GPT and
532+ not self.uuid):
533+ # Partition is part of a GPT partition table and doesn't have
534+ # a UUID set. Set the UUID so MAAS will know the UUID of the
535+ # partition on the created machine.
536+ self.uuid = uuid4()
537+ return super(Partition, self).save(*args, **kwargs)
538
539=== added file 'src/maasserver/models/tests/test_partition.py'
540--- src/maasserver/models/tests/test_partition.py 1970-01-01 00:00:00 +0000
541+++ src/maasserver/models/tests/test_partition.py 2015-03-12 03:23:40 +0000
542@@ -0,0 +1,56 @@
543+# Copyright 2015 Canonical Ltd. This software is licensed under the
544+# GNU Affero General Public License version 3 (see the file LICENSE).
545+
546+"""Tests for `Partition`."""
547+
548+from __future__ import (
549+ absolute_import,
550+ print_function,
551+ unicode_literals,
552+ )
553+
554+str = None
555+
556+__metaclass__ = type
557+__all__ = []
558+
559+from uuid import uuid4
560+
561+from maasserver.enum import PARTITION_TABLE_TYPE
562+from maasserver.testing.factory import factory
563+from maasserver.testing.testcase import MAASServerTestCase
564+
565+
566+class TestPartition(MAASServerTestCase):
567+ """Tests for the `Partition` model."""
568+
569+ def test_get_node_returns_partition_table_node(self):
570+ partition = factory.make_Partition()
571+ self.assertEquals(
572+ partition.partition_table.get_node(), partition.get_node())
573+
574+ def test_get_block_size_returns_partition_table_block_size(self):
575+ partition = factory.make_Partition()
576+ self.assertEquals(
577+ partition.partition_table.get_block_size(),
578+ partition.get_block_size())
579+
580+ def test_doesnt_set_uuid_if_partition_table_is_MBR(self):
581+ table = factory.make_PartitionTable(
582+ table_type=PARTITION_TABLE_TYPE.MBR)
583+ partition = factory.make_Partition(partition_table=table)
584+ self.assertIsNone(partition.uuid)
585+
586+ def test_set_uuid_if_partition_table_is_GPT(self):
587+ table = factory.make_PartitionTable(
588+ table_type=PARTITION_TABLE_TYPE.GPT)
589+ partition = factory.make_Partition(partition_table=table)
590+ self.assertIsNotNone(partition.uuid)
591+
592+ def test_save_doesnt_overwrite_uuid(self):
593+ uuid = uuid4()
594+ table = factory.make_PartitionTable(
595+ table_type=PARTITION_TABLE_TYPE.GPT)
596+ partition = factory.make_Partition(partition_table=table, uuid=uuid)
597+ partition.save()
598+ self.assertEquals('%s' % uuid, partition.uuid)
599
600=== modified file 'src/maasserver/testing/factory.py'
601--- src/maasserver/testing/factory.py 2015-03-12 03:23:40 +0000
602+++ src/maasserver/testing/factory.py 2015-03-12 03:23:40 +0000
603@@ -61,6 +61,7 @@
604 Node,
605 NodeGroup,
606 NodeGroupInterface,
607+ Partition,
608 PartitionTable,
609 PhysicalBlockDevice,
610 SSHKey,
611@@ -1091,6 +1092,21 @@
612 return PartitionTable.objects.create(
613 table_type=table_type, block_device=block_device)
614
615+ def make_Partition(
616+ self, partition_table=None, uuid=None, start_offset=None,
617+ size=None, bootable=None):
618+ if partition_table is None:
619+ partition_table = self.make_PartitionTable()
620+ if start_offset is None:
621+ start_offset = random.randint(0, partition_table.get_size() - 2)
622+ if size is None:
623+ size = random.randint(1, partition_table.get_size() - start_offset)
624+ if bootable is None:
625+ bootable = random.choice([True, False])
626+ return Partition.objects.create(
627+ partition_table=partition_table, uuid=uuid,
628+ start_offset=start_offset, size=size, bootable=bootable)
629+
630
631 # Create factory singleton.
632 factory = Factory()