Merge lp:~blake-rouse/maas/add-osystem-to-node-migration 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: 2320
Proposed branch: lp:~blake-rouse/maas/add-osystem-to-node-migration
Merge into: lp:~maas-committers/maas/trunk
Prerequisite: lp:~blake-rouse/maas/osystem-registry
Diff against target: 383 lines (+285/-8)
8 files modified
src/maasserver/migrations/0076_add_osystem_to_bootimage.py (+2/-2)
src/maasserver/migrations/0077_remove_null_for_bootsourceselection_release.py (+2/-2)
src/maasserver/migrations/0078_add_osystem_to_node.py (+270/-0)
src/maasserver/models/node.py (+8/-2)
src/maasserver/testing/factory.py (+0/-1)
src/maasserver/testing/osystems.py (+1/-1)
src/provisioningserver/boot/tftppath.py (+1/-0)
src/provisioningserver/driver/tests/test_os_ubuntu.py (+1/-0)
To merge this branch: bzr merge lp:~blake-rouse/maas/add-osystem-to-node-migration
Reviewer Review Type Date Requested Status
Raphaël Badin (community) Approve
Jason Hobbs (community) Approve
Review via email: mp+219514@code.launchpad.net

Commit message

Add osystem field to the node model.

Description of the change

This replaces the large merge of add-osystem-to-node, and separates it into 3 different mergers. This is the first one. It contains only the migration of adding osystem to the node model.

Note: osystem was used throughout the code instead of os, as the python os module would conflict throughout the code base.

To post a comment you must log in.
Revision history for this message
Jason Hobbs (jason-hobbs) wrote :

Looks good, just a few comments.

[1]

Be sure to run 'make format',
src/maasserver/migrations/0077_add_osystem_to_node.py gets cleaned up a
bit by it.

[2]

=== added file 'src/maasserver/migrations/0077_add_osystem_to_node.py'
--- src/maasserver/migrations/0077_add_osystem_to_node.py 1970-01-01 00:00:00 +0000
+++ src/maasserver/migrations/0077_add_osystem_to_node.py 2014-05-14 12:45:06 +0000

There is already another 0077 migration, so you'll have to merge and
regenerate this.

[3]

+ def forwards(self, orm):
+ # Adding field 'Node.osystem'
+ db.add_column(u'maasserver_node', 'osystem',
+ self.gf('django.db.models.fields.CharField')(default=u'', max_length=20, null=True, blank=True),
+ keep_default=False)

The max_length for Node's osystem field (20) doesn't match the max
length for BootImage's osystem field (255). If they're holding the
same set of values they should have the same types.

Also, Is there a set of osystem values a Node's osystem value should be
constrained to? Should a node always have an osystem that some BootImage
has, or is there some set they should both be constrained to?

[4]

There should be at least some basic test involving the osystem field.

review: Needs Fixing
Revision history for this message
Blake Rouse (blake-rouse) wrote :

[1,2] Fixed

[3] Using 20 as that was what was chosen by the core team for the release field in BootImages, whether that is the correct thing, idk.

[4] At this current state in the branch the field is not used for testing. This merge requests uses the field and provides test for it. If you would like to review that, :)

https://code.launchpad.net/~blake-rouse/maas/add-osystem-to-node-form-api

Revision history for this message
Jason Hobbs (jason-hobbs) wrote :

[3] Still seems broken to me, but I get now that you're following the same pattern of fields being size 20 in Node and size 255 in BootImages that was there already. I guess this can be fixed later if it turns out to be an issue.

[4] I think we should in general add tests when we add code, but this was a retroactive breakdown, so makes sense.

Thanks!

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

[0]

As Jason pointed out, it's always best to land tests with the code they test. For new models, a basic test is enough. You also might want to add a make_<my_model> utility in the test factory (src/maasserver/testing/factory.py)

[1]

See my inline comment about using null=True on Charfields.

[2]

As Jason pointed out, don't forget to run:
$ ./utilities/format-new-and-modified-imports
and
$ make lint
before you submit a branch.

[3]

Please update the Node's docstring.

review: Approve
Revision history for this message
Julian Edwards (julian-edwards) wrote :

On 15/05/14 00:23, Jason Hobbs wrote:
> Be sure to run 'make format',
> src/maasserver/migrations/0077_add_osystem_to_node.py gets cleaned up a
> bit by it.

My personal thought on these is that since they are autogenerated, it
really doesn't matter about formatting.

Revision history for this message
Jason Hobbs (jason-hobbs) wrote :

On Wed, May 14, 2014 at 7:06 PM, Julian Edwards <
<email address hidden>> wrote:

> On 15/05/14 00:23, Jason Hobbs wrote:
> > Be sure to run 'make format',
> > src/maasserver/migrations/0077_add_osystem_to_node.py gets cleaned up a
> > bit by it.
>
> My personal thought on these is that since they are autogenerated, it
> really doesn't matter about formatting.

They'll get reformatted the next time someone else runs 'make format',
which can be annoying. If we don't care about formatting on them, 'make
format' should ignore them I suppose.

Jason

Revision history for this message
Julian Edwards (julian-edwards) wrote :

On 15/05/14 10:57, Jason Hobbs wrote:
> On Wed, May 14, 2014 at 7:06 PM, Julian Edwards <
> <email address hidden>> wrote:
>
>> On 15/05/14 00:23, Jason Hobbs wrote:
>>> Be sure to run 'make format',
>>> src/maasserver/migrations/0077_add_osystem_to_node.py gets cleaned up a
>>> bit by it.
>>
>> My personal thought on these is that since they are autogenerated, it
>> really doesn't matter about formatting.
>
>
> They'll get reformatted the next time someone else runs 'make format',
> which can be annoying. If we don't care about formatting on them, 'make
> format' should ignore them I suppose.
>
> Jason
>

Yes, that's what I was intimating. But someone needs to be bothered to
make that tool ignore migrations.... :)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/migrations/0076_add_osystem_to_bootimage.py'
2--- src/maasserver/migrations/0076_add_osystem_to_bootimage.py 2014-05-02 19:21:43 +0000
3+++ src/maasserver/migrations/0076_add_osystem_to_bootimage.py 2014-05-14 15:21:48 +0000
4@@ -1,8 +1,8 @@
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.db import db
10 from south.v2 import SchemaMigration
11-from django.db import models
12
13
14 class Migration(SchemaMigration):
15
16=== modified file 'src/maasserver/migrations/0077_remove_null_for_bootsourceselection_release.py'
17--- src/maasserver/migrations/0077_remove_null_for_bootsourceselection_release.py 2014-05-14 08:32:13 +0000
18+++ src/maasserver/migrations/0077_remove_null_for_bootsourceselection_release.py 2014-05-14 15:21:48 +0000
19@@ -1,8 +1,8 @@
20+from django.db import models
21+from south.db import db
22 # -*- coding: utf-8 -*-
23 from south.utils import datetime_utils as datetime
24-from south.db import db
25 from south.v2 import SchemaMigration
26-from django.db import models
27
28
29 class Migration(SchemaMigration):
30
31=== added file 'src/maasserver/migrations/0078_add_osystem_to_node.py'
32--- src/maasserver/migrations/0078_add_osystem_to_node.py 1970-01-01 00:00:00 +0000
33+++ src/maasserver/migrations/0078_add_osystem_to_node.py 2014-05-14 15:21:48 +0000
34@@ -0,0 +1,270 @@
35+from django.db import models
36+from south.db import db
37+# -*- coding: utf-8 -*-
38+from south.utils import datetime_utils as datetime
39+from south.v2 import SchemaMigration
40+
41+
42+class Migration(SchemaMigration):
43+
44+ def forwards(self, orm):
45+ # Adding field 'Node.osystem'
46+ db.add_column(u'maasserver_node', 'osystem',
47+ self.gf('django.db.models.fields.CharField')(default=u'', max_length=20, blank=True),
48+ keep_default=False)
49+
50+
51+ # Changing field 'Node.distro_series'
52+ db.alter_column(u'maasserver_node', 'distro_series', self.gf('django.db.models.fields.CharField')(max_length=20))
53+
54+ def backwards(self, orm):
55+ # Deleting field 'Node.osystem'
56+ db.delete_column(u'maasserver_node', 'osystem')
57+
58+
59+ # Changing field 'Node.distro_series'
60+ db.alter_column(u'maasserver_node', 'distro_series', self.gf('django.db.models.fields.CharField')(max_length=20, null=True))
61+
62+ models = {
63+ u'auth.group': {
64+ 'Meta': {'object_name': 'Group'},
65+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
66+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
67+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
68+ },
69+ u'auth.permission': {
70+ 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
71+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
72+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
73+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
74+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
75+ },
76+ u'auth.user': {
77+ 'Meta': {'object_name': 'User'},
78+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
79+ 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}),
80+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
81+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
82+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
83+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
84+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
85+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
86+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
87+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
88+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
89+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
90+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
91+ },
92+ u'contenttypes.contenttype': {
93+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
94+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
95+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
96+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
97+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
98+ },
99+ u'maasserver.bootimage': {
100+ 'Meta': {'unique_together': "((u'nodegroup', u'osystem', u'architecture', u'subarchitecture', u'release', u'purpose', u'label'),)", 'object_name': 'BootImage'},
101+ 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
102+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
103+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
104+ 'label': ('django.db.models.fields.CharField', [], {'default': "u'release'", 'max_length': '255'}),
105+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
106+ 'osystem': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
107+ 'purpose': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
108+ 'release': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
109+ 'subarchitecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
110+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
111+ },
112+ u'maasserver.bootsource': {
113+ 'Meta': {'object_name': 'BootSource'},
114+ 'cluster': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}),
115+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
116+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
117+ 'keyring_data': ('django.db.models.fields.BinaryField', [], {'blank': 'True'}),
118+ 'keyring_filename': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
119+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
120+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
121+ },
122+ u'maasserver.bootsourceselection': {
123+ 'Meta': {'object_name': 'BootSourceSelection'},
124+ 'arches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
125+ 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}),
126+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
127+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
128+ 'labels': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
129+ 'release': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
130+ 'subarches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
131+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
132+ },
133+ u'maasserver.componenterror': {
134+ 'Meta': {'object_name': 'ComponentError'},
135+ 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}),
136+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
137+ 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
138+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
139+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
140+ },
141+ u'maasserver.config': {
142+ 'Meta': {'object_name': 'Config'},
143+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
144+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
145+ 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'})
146+ },
147+ u'maasserver.dhcplease': {
148+ 'Meta': {'object_name': 'DHCPLease'},
149+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
150+ 'ip': ('django.db.models.fields.IPAddressField', [], {'unique': 'True', 'max_length': '15'}),
151+ 'mac': ('maasserver.fields.MACAddressField', [], {}),
152+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"})
153+ },
154+ u'maasserver.downloadprogress': {
155+ 'Meta': {'object_name': 'DownloadProgress'},
156+ 'bytes_downloaded': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
157+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
158+ 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}),
159+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
160+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
161+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
162+ 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
163+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
164+ },
165+ u'maasserver.filestorage': {
166+ 'Meta': {'unique_together': "((u'filename', u'owner'),)", 'object_name': 'FileStorage'},
167+ 'content': ('metadataserver.fields.BinaryField', [], {'blank': 'True'}),
168+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
169+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
170+ 'key': ('django.db.models.fields.CharField', [], {'default': "u'ab43bb24-db7a-11e3-81cc-bcee7b78dc5b'", 'unique': 'True', 'max_length': '36'}),
171+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
172+ },
173+ u'maasserver.macaddress': {
174+ 'Meta': {'object_name': 'MACAddress'},
175+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
176+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
177+ 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}),
178+ 'networks': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Network']", 'symmetrical': 'False', 'blank': 'True'}),
179+ 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}),
180+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
181+ },
182+ u'maasserver.network': {
183+ 'Meta': {'object_name': 'Network'},
184+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
185+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
186+ 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
187+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
188+ 'netmask': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
189+ 'vlan_tag': ('django.db.models.fields.PositiveSmallIntegerField', [], {'unique': 'True', 'null': 'True', 'blank': 'True'})
190+ },
191+ u'maasserver.node': {
192+ 'Meta': {'object_name': 'Node'},
193+ 'agent_name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
194+ 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '31'}),
195+ 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
196+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
197+ 'distro_series': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
198+ 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
199+ 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}),
200+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
201+ 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
202+ 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
203+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}),
204+ 'osystem': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
205+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
206+ 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
207+ 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}),
208+ 'routers': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'macaddr'", 'null': 'True', 'blank': 'True'}),
209+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}),
210+ 'storage': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
211+ 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-ab42b030-db7a-11e3-81cc-bcee7b78dc5b'", 'unique': 'True', 'max_length': '41'}),
212+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}),
213+ 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'null': 'True'}),
214+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
215+ 'zone': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Zone']", 'on_delete': 'models.SET_DEFAULT'})
216+ },
217+ u'maasserver.nodegroup': {
218+ 'Meta': {'object_name': 'NodeGroup'},
219+ 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}),
220+ 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'unique': 'True'}),
221+ 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}),
222+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
223+ 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
224+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
225+ 'maas_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
226+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
227+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
228+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
229+ 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
230+ },
231+ u'maasserver.nodegroupinterface': {
232+ 'Meta': {'unique_together': "((u'nodegroup', u'interface'),)", 'object_name': 'NodeGroupInterface'},
233+ 'broadcast_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
234+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
235+ 'foreign_dhcp_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
236+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
237+ 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
238+ 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
239+ 'ip_range_high': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
240+ 'ip_range_low': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
241+ 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
242+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
243+ 'router_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
244+ 'subnet_mask': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
245+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
246+ },
247+ u'maasserver.sshkey': {
248+ 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'},
249+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
250+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
251+ 'key': ('django.db.models.fields.TextField', [], {}),
252+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
253+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
254+ },
255+ u'maasserver.tag': {
256+ 'Meta': {'object_name': 'Tag'},
257+ 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
258+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
259+ 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
260+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
261+ 'kernel_opts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
262+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
263+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
264+ },
265+ u'maasserver.userprofile': {
266+ 'Meta': {'object_name': 'UserProfile'},
267+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
268+ 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'})
269+ },
270+ u'maasserver.zone': {
271+ 'Meta': {'ordering': "[u'name']", 'object_name': 'Zone'},
272+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
273+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
274+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
275+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
276+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
277+ },
278+ u'piston.consumer': {
279+ 'Meta': {'object_name': 'Consumer'},
280+ 'description': ('django.db.models.fields.TextField', [], {}),
281+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
282+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
283+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
284+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
285+ 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}),
286+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': u"orm['auth.User']"})
287+ },
288+ u'piston.token': {
289+ 'Meta': {'object_name': 'Token'},
290+ 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
291+ 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
292+ 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Consumer']"}),
293+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
294+ 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
295+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
296+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
297+ 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1400080565L'}),
298+ 'token_type': ('django.db.models.fields.IntegerField', [], {}),
299+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': u"orm['auth.User']"}),
300+ 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'})
301+ }
302+ }
303+
304+ complete_apps = ['maasserver']
305
306=== modified file 'src/maasserver/models/node.py'
307--- src/maasserver/models/node.py 2014-05-09 06:04:54 +0000
308+++ src/maasserver/models/node.py 2014-05-14 15:21:48 +0000
309@@ -450,6 +450,10 @@
310 :ivar status: This `Node`'s status. See the vocabulary
311 :class:`NODE_STATUS`.
312 :ivar owner: This `Node`'s owner if it's in use, None otherwise.
313+ :ivar osystem: This `Node`'s booting operating system, if it's blank then
314+ the default_osystem will be used.
315+ :ivar distro_series: This `Node`'s booting distro series, if
316+ it's blank then the default_distro_series will be used.
317 :ivar power_type: The power type that determines how this
318 node will be powered on. Its value must match a power driver template
319 name.
320@@ -477,9 +481,11 @@
321 owner = ForeignKey(
322 User, default=None, blank=True, null=True, editable=False)
323
324+ osystem = CharField(
325+ max_length=20, blank=True, default='')
326+
327 distro_series = CharField(
328- max_length=20, choices=DISTRO_SERIES_CHOICES, null=True,
329- blank=True, default='')
330+ max_length=20, choices=DISTRO_SERIES_CHOICES, blank=True, default='')
331
332 architecture = CharField(max_length=31, blank=False)
333
334
335=== modified file 'src/maasserver/testing/factory.py'
336--- src/maasserver/testing/factory.py 2014-05-13 18:55:36 +0000
337+++ src/maasserver/testing/factory.py 2014-05-14 15:21:48 +0000
338@@ -59,7 +59,6 @@
339 NodeCommissionResult,
340 )
341 from netaddr import IPAddress
342-
343 # XXX 2014-05-13 blake-rouse bug=1319143
344 # Need to not import directly, use RPC to info from cluster.
345 from provisioningserver.driver import OperatingSystemRegistry
346
347=== modified file 'src/maasserver/testing/osystems.py'
348--- src/maasserver/testing/osystems.py 2014-05-01 19:25:38 +0000
349+++ src/maasserver/testing/osystems.py 2014-05-14 15:21:48 +0000
350@@ -21,8 +21,8 @@
351
352 from maasserver import forms
353 from maasserver.testing.factory import factory
354+from provisioningserver.boot.tests.test_tftppath import make_osystem
355 from provisioningserver.driver import BOOT_IMAGE_PURPOSE
356-from provisioningserver.boot.tests.test_tftppath import make_osystem
357
358
359 def make_osystem_with_releases(testcase, osystem_name=None, releases=None):
360
361=== modified file 'src/provisioningserver/boot/tftppath.py'
362--- src/provisioningserver/boot/tftppath.py 2014-05-01 18:54:22 +0000
363+++ src/provisioningserver/boot/tftppath.py 2014-05-14 15:21:48 +0000
364@@ -27,6 +27,7 @@
365
366 from provisioningserver.driver import OperatingSystemRegistry
367
368+
369 logger = getLogger(__name__)
370
371
372
373=== modified file 'src/provisioningserver/driver/tests/test_os_ubuntu.py'
374--- src/provisioningserver/driver/tests/test_os_ubuntu.py 2014-05-01 19:25:38 +0000
375+++ src/provisioningserver/driver/tests/test_os_ubuntu.py 2014-05-14 15:21:48 +0000
376@@ -15,6 +15,7 @@
377 __all__ = []
378
379 from itertools import product
380+
381 from maastesting.factory import factory
382 from maastesting.testcase import MAASTestCase
383 from provisioningserver.driver import BOOT_IMAGE_PURPOSE