Merge lp:~rvb/maas/add-network-node-rel into lp:~maas-committers/maas/trunk

Proposed by Raphaël Badin
Status: Rejected
Rejected by: Raphaël Badin
Proposed branch: lp:~rvb/maas/add-network-node-rel
Merge into: lp:~maas-committers/maas/trunk
Prerequisite: lp:~jtv/maas/vlan-to-network
Diff against target: 314 lines (+282/-0) (has conflicts)
3 files modified
src/maasserver/migrations/0066_rename_vlan_add_link_node_network.py (+268/-0)
src/maasserver/models/node.py (+3/-0)
src/maasserver/models/tests/test_node.py (+11/-0)
Contents conflict in src/maasserver/migrations/0066_replace_vlan_with_network.py
To merge this branch: bzr merge lp:~rvb/maas/add-network-node-rel
Reviewer Review Type Date Requested Status
Julian Edwards (community) Approve
Review via email: mp+204506@code.launchpad.net

Commit message

Add a relation between nodes and networks.

Description of the change

By using a ManyToManyField field, we let Django take care of the creation of the intermediate table.

To post a comment you must log in.
Revision history for this message
Julian Edwards (julian-edwards) wrote :

Ok but a small gripe: the number of migrations we have is ridiculous. Can you merge this with the previous one of jtv's please? It's not been released to Trusty yet so is safe to do so.

review: Approve
lp:~rvb/maas/add-network-node-rel updated
1881. By Raphaël Badin

Fold two migrations into one.

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

> Ok but a small gripe: the number of migrations we have is ridiculous. Can you
> merge this with the previous one of jtv's please? It's not been released to
> Trusty yet so is safe to do so.

Well okay… although this is not buying us much: one migration out of more than 60.

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

Hum, the MP says it has a conflict and the diff doesn't show one. I'm guessing this is because the pre-req has been merged or something. I'm sick of fighting with bzr so I'll just abandon this MP and start afresh.

Revision history for this message
Raphaël Badin (rvb) wrote :
Revision history for this message
Julian Edwards (julian-edwards) wrote :

On Tuesday 04 Feb 2014 09:01:05 you wrote:
> > Ok but a small gripe: the number of migrations we have is ridiculous. Can
> > you merge this with the previous one of jtv's please? It's not been
> > released to Trusty yet so is safe to do so.
>
> Well okay… although this is not buying us much: one migration out of more
> than 60.

It's the principle as much as anything. Installing a package from new results
in a huge set of migrations. I'd really like some sort of normalisation for
this case as well.

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

I feel we could buy ourselves a world of pain by trying to consolidate migrations. It's not really something that improves MAAS over the longer run. Let's live with it and wait to see if South or something else comes up with a decent "skip to the end" solution that we can use without having to maintain it too.

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

On Tuesday 04 Feb 2014 11:40:17 you wrote:
> I feel we could buy ourselves a world of pain by trying to consolidate
> migrations. It's not really something that improves MAAS over the longer
> run. Let's live with it and wait to see if South or something else comes up
> with a decent "skip to the end" solution that we can use without having to
> maintain it too.

This was more of a "let's be a bit more thoughtful about how we do this".
Landing two separate branches that incrementally change a new model isn't too
smart.

Otherwise I agree that messing around trying to force things could be painful.

Unmerged revisions

1881. By Raphaël Badin

Fold two migrations into one.

1880. By Raphaël Badin

Add link node <-> network.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'src/maasserver/migrations/0066_rename_vlan_add_link_node_network.py'
2--- src/maasserver/migrations/0066_rename_vlan_add_link_node_network.py 1970-01-01 00:00:00 +0000
3+++ src/maasserver/migrations/0066_rename_vlan_add_link_node_network.py 2014-02-04 09:00:58 +0000
4@@ -0,0 +1,268 @@
5+# -*- coding: utf-8 -*-
6+import datetime
7+
8+from django.db import models
9+from south.db import db
10+from south.v2 import SchemaMigration
11+
12+
13+class Migration(SchemaMigration):
14+
15+ def forwards(self, orm):
16+ # Deleting model 'Vlan'
17+ db.delete_table(u'maasserver_vlan')
18+
19+ # Adding model 'Network'
20+ db.create_table(u'maasserver_network', (
21+ (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
22+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
23+ ('ip', self.gf('django.db.models.fields.GenericIPAddressField')(unique=True, max_length=39)),
24+ ('netmask', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39)),
25+ ('vlan_tag', self.gf('django.db.models.fields.PositiveSmallIntegerField')(unique=True)),
26+ ('description', self.gf('django.db.models.fields.CharField')(default=u'', max_length=255, blank=True)),
27+ ))
28+ db.send_create_signal(u'maasserver', ['Network'])
29+
30+ # Adding M2M table for field networks on 'Node'
31+ db.create_table(u'maasserver_node_networks', (
32+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
33+ ('node', models.ForeignKey(orm[u'maasserver.node'], null=False)),
34+ ('network', models.ForeignKey(orm[u'maasserver.network'], null=False))
35+ ))
36+ db.create_unique(u'maasserver_node_networks', ['node_id', 'network_id'])
37+
38+
39+ def backwards(self, orm):
40+ # Adding model 'Vlan'
41+ db.create_table(u'maasserver_vlan', (
42+ ('tag', self.gf('django.db.models.fields.PositiveSmallIntegerField')(unique=True)),
43+ ('description', self.gf('django.db.models.fields.CharField')(default=u'', max_length=255, blank=True)),
44+ (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
45+ ))
46+ db.send_create_signal(u'maasserver', ['Vlan'])
47+
48+ # Deleting model 'Network'
49+ db.delete_table(u'maasserver_network')
50+
51+ # Removing M2M table for field networks on 'Node'
52+ db.delete_table('maasserver_node_networks')
53+
54+
55+ models = {
56+ u'auth.group': {
57+ 'Meta': {'object_name': 'Group'},
58+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
59+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
60+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
61+ },
62+ u'auth.permission': {
63+ 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
64+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
65+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
66+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
67+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
68+ },
69+ u'auth.user': {
70+ 'Meta': {'object_name': 'User'},
71+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
72+ 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}),
73+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
74+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
75+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
76+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
77+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
78+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
79+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
80+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
81+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
82+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
83+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
84+ },
85+ u'contenttypes.contenttype': {
86+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
87+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
88+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
89+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
90+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
91+ },
92+ u'maasserver.bootimage': {
93+ 'Meta': {'unique_together': "((u'nodegroup', u'architecture', u'subarchitecture', u'release', u'purpose'),)", 'object_name': 'BootImage'},
94+ 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
95+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
96+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
97+ 'purpose': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
98+ 'release': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
99+ 'subarchitecture': ('django.db.models.fields.CharField', [], {'max_length': '255'})
100+ },
101+ u'maasserver.componenterror': {
102+ 'Meta': {'object_name': 'ComponentError'},
103+ 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}),
104+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
105+ 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
106+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
107+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
108+ },
109+ u'maasserver.config': {
110+ 'Meta': {'object_name': 'Config'},
111+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
112+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
113+ 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'})
114+ },
115+ u'maasserver.dhcplease': {
116+ 'Meta': {'object_name': 'DHCPLease'},
117+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
118+ 'ip': ('django.db.models.fields.IPAddressField', [], {'unique': 'True', 'max_length': '15'}),
119+ 'mac': ('maasserver.fields.MACAddressField', [], {}),
120+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"})
121+ },
122+ u'maasserver.downloadprogress': {
123+ 'Meta': {'object_name': 'DownloadProgress'},
124+ 'bytes_downloaded': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
125+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
126+ 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}),
127+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
128+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
129+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
130+ 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
131+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
132+ },
133+ u'maasserver.filestorage': {
134+ 'Meta': {'unique_together': "((u'filename', u'owner'),)", 'object_name': 'FileStorage'},
135+ 'content': ('metadataserver.fields.BinaryField', [], {'blank': 'True'}),
136+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
137+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
138+ 'key': ('django.db.models.fields.CharField', [], {'default': "u'6b10b1a2-8d77-11e3-a34b-9c4e363b1c94'", 'unique': 'True', 'max_length': '36'}),
139+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
140+ },
141+ u'maasserver.macaddress': {
142+ 'Meta': {'object_name': 'MACAddress'},
143+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
144+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
145+ 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}),
146+ 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}),
147+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
148+ },
149+ u'maasserver.network': {
150+ 'Meta': {'object_name': 'Network'},
151+ 'description': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
152+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
153+ 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
154+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
155+ 'netmask': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
156+ 'vlan_tag': ('django.db.models.fields.PositiveSmallIntegerField', [], {'unique': 'True'})
157+ },
158+ u'maasserver.node': {
159+ 'Meta': {'object_name': 'Node'},
160+ 'after_commissioning_action': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
161+ 'agent_name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
162+ 'architecture': ('django.db.models.fields.CharField', [], {'default': "u'i386/generic'", 'max_length': '31'}),
163+ 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
164+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
165+ 'distro_series': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'null': 'True', 'blank': 'True'}),
166+ 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
167+ 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}),
168+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
169+ 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
170+ 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
171+ 'networks': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Network']", 'symmetrical': 'False', 'blank': 'True'}),
172+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}),
173+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
174+ 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
175+ 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}),
176+ 'routers': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'macaddr'", 'null': 'True', 'blank': 'True'}),
177+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}),
178+ 'storage': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
179+ 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-6b0eab46-8d77-11e3-a34b-9c4e363b1c94'", 'unique': 'True', 'max_length': '41'}),
180+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}),
181+ 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'null': 'True'}),
182+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
183+ 'zone': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Zone']", 'on_delete': 'models.SET_DEFAULT'})
184+ },
185+ u'maasserver.nodegroup': {
186+ 'Meta': {'object_name': 'NodeGroup'},
187+ 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}),
188+ 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'unique': 'True'}),
189+ 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}),
190+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
191+ 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
192+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
193+ 'maas_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
194+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
195+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
196+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
197+ 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
198+ },
199+ u'maasserver.nodegroupinterface': {
200+ 'Meta': {'unique_together': "((u'nodegroup', u'interface'),)", 'object_name': 'NodeGroupInterface'},
201+ 'broadcast_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
202+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
203+ 'foreign_dhcp_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
204+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
205+ 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
206+ 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
207+ 'ip_range_high': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
208+ 'ip_range_low': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
209+ 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
210+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
211+ 'router_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
212+ 'subnet_mask': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
213+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
214+ },
215+ u'maasserver.sshkey': {
216+ 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'},
217+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
218+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
219+ 'key': ('django.db.models.fields.TextField', [], {}),
220+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
221+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
222+ },
223+ u'maasserver.tag': {
224+ 'Meta': {'object_name': 'Tag'},
225+ 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
226+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
227+ 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
228+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
229+ 'kernel_opts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
230+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
231+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
232+ },
233+ u'maasserver.userprofile': {
234+ 'Meta': {'object_name': 'UserProfile'},
235+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
236+ 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'})
237+ },
238+ u'maasserver.zone': {
239+ 'Meta': {'ordering': "[u'name']", 'object_name': 'Zone'},
240+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
241+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
242+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
243+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
244+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
245+ },
246+ u'piston.consumer': {
247+ 'Meta': {'object_name': 'Consumer'},
248+ 'description': ('django.db.models.fields.TextField', [], {}),
249+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
250+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
251+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
252+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
253+ 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}),
254+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': u"orm['auth.User']"})
255+ },
256+ u'piston.token': {
257+ 'Meta': {'object_name': 'Token'},
258+ 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
259+ 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
260+ 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Consumer']"}),
261+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
262+ 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
263+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
264+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
265+ 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1391502978L'}),
266+ 'token_type': ('django.db.models.fields.IntegerField', [], {}),
267+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': u"orm['auth.User']"}),
268+ 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'})
269+ }
270+ }
271+
272+ complete_apps = ['maasserver']
273\ No newline at end of file
274
275=== renamed file 'src/maasserver/migrations/0066_replace_vlan_with_network.py' => 'src/maasserver/migrations/0066_replace_vlan_with_network.py.THIS'
276=== modified file 'src/maasserver/models/node.py'
277--- src/maasserver/models/node.py 2014-01-31 15:12:20 +0000
278+++ src/maasserver/models/node.py 2014-02-04 09:00:58 +0000
279@@ -64,6 +64,7 @@
280 JSONObjectField,
281 MAC,
282 )
283+from maasserver.models import Network
284 from maasserver.models.cleansave import CleanSave
285 from maasserver.models.config import Config
286 from maasserver.models.dhcplease import DHCPLease
287@@ -488,6 +489,8 @@
288
289 netboot = BooleanField(default=True)
290
291+ networks = ManyToManyField(Network, blank=True)
292+
293 # This field can't be null, but we can't enforce that in the
294 # database schema because we can only create the default value from
295 # a complete schema, after schema migration. We can't use custom
296
297=== modified file 'src/maasserver/models/tests/test_node.py'
298--- src/maasserver/models/tests/test_node.py 2014-02-01 05:54:22 +0000
299+++ src/maasserver/models/tests/test_node.py 2014-02-04 09:00:58 +0000
300@@ -1166,3 +1166,14 @@
301 node = factory.make_node(netboot=True)
302 node.set_netboot(False)
303 self.assertFalse(node.netboot)
304+
305+ def test_node_not_in_any_network_by_default(self):
306+ node = factory.make_node()
307+ self.assertItemsEqual([], node.networks.all())
308+
309+ def test_node_can_be_multiple_networks(self):
310+ node = factory.make_node()
311+ networks = [
312+ factory.make_network() for i in range(3)]
313+ node.networks.add(*networks)
314+ self.assertItemsEqual(networks, reload_object(node).networks.all())