Merge lp:~julian-edwards/maas/cluster-interfaces-as-networks-2 into lp:~maas-committers/maas/trunk

Proposed by Julian Edwards
Status: Merged
Approved by: Julian Edwards
Approved revision: no longer in the source branch.
Merged at revision: 2694
Proposed branch: lp:~julian-edwards/maas/cluster-interfaces-as-networks-2
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 453 lines (+404/-20)
2 files modified
src/maasserver/forms.py (+33/-20)
src/maasserver/migrations/0099_convert_cluster_interfaces_to_networks.py (+371/-0)
To merge this branch: bzr merge lp:~julian-edwards/maas/cluster-interfaces-as-networks-2
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+230411@code.launchpad.net

Commit message

Add a data migration which sets up Network objects for all the existing cluster interfaces.

Description of the change

This re-uses the existing code to do the actual model creation, which I factored out of the form's save() method. I had to change the code slightly because you can't access non-column properties on the model (it's not the same model!). The existing tests still pass.

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

I think this needs fixing, but I'm not sure, and I don't want to block you on that.

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

On Tuesday 12 Aug 2014 10:52:32 you wrote:
> Review: Approve
>
> I think this needs fixing, but I'm not sure, and I don't want to block you
> on that.

It did...!

> You (well, the author) could use braces:

Braces Schmaces. /me shakes fist at linter

Thanks ;)
> Not your code (afaik), but the trailing clause here is terrible. I suggest
> removing ", or it could be another error" entirely. Also, punctuation:
> missing full-stop.

Actually it was my code. :( My cognitive ability has gone down the damn drain
in the last few weeks and with it my ability to write as coherently. I hope
it will get better soon ...

> This looks buggy: it returns the unsaved network object when there's an
> error. Should that `pass` be a `return None`?

It should, and that's a great spot.

> This is only tested indirectly. Are there any tests that you could repurpose
> to unit test this function alone?

I actually think the indirection is fine in this case. It's one of those
scenarios where I think you'd end up doing the same testing twice, unless you
cheat with a Mock to assert whether the code is getting called from the form.
And I hate hate hate those tests.

If you have any other suggestions I am all ears, but I'll do a post-land fix
if you want.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/forms.py'
2--- src/maasserver/forms.py 2014-08-11 10:28:42 +0000
3+++ src/maasserver/forms.py 2014-08-14 05:57:42 +0000
4@@ -18,6 +18,7 @@
5 "BootSourceForm",
6 "BootSourceSelectionForm",
7 "BulkNodeActionForm",
8+ "create_Network_from_NodeGroupInterface",
9 "CommissioningForm",
10 "CommissioningScriptForm",
11 "DownloadProgressForm",
12@@ -1153,6 +1154,37 @@
13 return True
14
15
16+def create_Network_from_NodeGroupInterface(interface):
17+ """Given a `NodeGroupInterface`, create its Network counterpart."""
18+ # This method cannot use non-orm model properties because it needs
19+ # to be used in a data migration, where they won't work.
20+ if not interface.subnet_mask:
21+ # Can be None or empty string, do nothing if so.
22+ return
23+
24+ name, vlan_tag = get_name_and_vlan_from_cluster_interface(interface)
25+ ipnetwork = make_network(interface.ip, interface.subnet_mask)
26+ network = Network(
27+ name=name,
28+ ip=unicode(ipnetwork.network),
29+ netmask=unicode(ipnetwork.netmask),
30+ vlan_tag=vlan_tag,
31+ description=(
32+ "Auto created when creating interface %s on cluster "
33+ "%s" % (interface.name, interface.nodegroup.name)),
34+ )
35+ try:
36+ network.save()
37+ except ValidationError as e:
38+ # It probably already exists, keep calm and carry on.
39+ maaslog.warning(
40+ "Failed to create Network when adding/editing cluster "
41+ "interface %s with error [%s]. This is OK if it already "
42+ "exists." % (name, unicode(e)))
43+ return
44+ return network
45+
46+
47 class NodeGroupInterfaceForm(ModelForm):
48
49 management = forms.TypedChoiceField(
50@@ -1187,26 +1219,7 @@
51 interface = super(NodeGroupInterfaceForm, self).save(*args, **kwargs)
52 if interface.network is None:
53 return interface
54- name, vlan_tag = get_name_and_vlan_from_cluster_interface(interface)
55- network = Network(
56- name=name,
57- ip=unicode(interface.network.network),
58- netmask=unicode(interface.network.netmask),
59- vlan_tag=vlan_tag,
60- # I bloody hate the damn linter. It actually prefers this.
61- description="Auto created when creating interface %s on "
62- "cluster %s" % (
63- interface.name, interface.nodegroup.name),
64- )
65- try:
66- network.save()
67- except ValidationError as e:
68- # It probably already exists, keep calm and carry on.
69- maaslog.warning(
70- "Failed to create Network when adding/editing cluster "
71- "interface %s with error [%s]. This is OK if it already "
72- "exists, or it could be another error" % (name, unicode(e)))
73- pass
74+ create_Network_from_NodeGroupInterface(interface)
75 return interface
76
77 def compute_name(self):
78
79=== added file 'src/maasserver/migrations/0099_convert_cluster_interfaces_to_networks.py'
80--- src/maasserver/migrations/0099_convert_cluster_interfaces_to_networks.py 1970-01-01 00:00:00 +0000
81+++ src/maasserver/migrations/0099_convert_cluster_interfaces_to_networks.py 2014-08-14 05:57:42 +0000
82@@ -0,0 +1,371 @@
83+# -*- coding: utf-8 -*-
84+from django.db import models
85+from maasserver.forms import create_Network_from_NodeGroupInterface
86+from south.db import db
87+from south.utils import datetime_utils as datetime
88+from south.v2 import DataMigration
89+
90+
91+class Migration(DataMigration):
92+
93+ def forwards(self, orm):
94+ "Write your forwards methods here."
95+ # Note: Remember to use orm['appname.ModelName'] rather than "from appname.models..."
96+ interfaces = orm['maasserver.NodeGroupInterface'].objects.all()
97+ for interface in interfaces:
98+ create_Network_from_NodeGroupInterface(interface)
99+
100+ def backwards(self, orm):
101+ "Write your backwards methods here."
102+ # No point going backwards and it's probably dangerous to delete
103+ # Networks. The forwards code is idempotent anyway.
104+ pass
105+
106+ models = {
107+ u'auth.group': {
108+ 'Meta': {'object_name': 'Group'},
109+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
110+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
111+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
112+ },
113+ u'auth.permission': {
114+ 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
115+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
116+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
117+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
118+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
119+ },
120+ u'auth.user': {
121+ 'Meta': {'object_name': 'User'},
122+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
123+ 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}),
124+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
125+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
126+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
127+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
128+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
129+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
130+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
131+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
132+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
133+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
134+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
135+ },
136+ u'contenttypes.contenttype': {
137+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
138+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
139+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
140+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
141+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
142+ },
143+ u'maasserver.bootimage': {
144+ 'Meta': {'unique_together': "((u'nodegroup', u'osystem', u'architecture', u'subarchitecture', u'release', u'purpose', u'label'),)", 'object_name': 'BootImage'},
145+ 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
146+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
147+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
148+ 'label': ('django.db.models.fields.CharField', [], {'default': "u'release'", 'max_length': '255'}),
149+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
150+ 'osystem': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
151+ 'purpose': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
152+ 'release': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
153+ 'subarchitecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
154+ 'supported_subarches': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
155+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
156+ 'xinstall_path': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
157+ 'xinstall_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '30', 'null': 'True', 'blank': 'True'})
158+ },
159+ u'maasserver.bootresource': {
160+ 'Meta': {'unique_together': "((u'rtype', u'name', u'architecture'),)", 'object_name': 'BootResource'},
161+ 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
162+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
163+ 'extra': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
164+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
165+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
166+ 'rtype': ('django.db.models.fields.IntegerField', [], {'max_length': '10'}),
167+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
168+ },
169+ u'maasserver.bootresourcefile': {
170+ 'Meta': {'unique_together': "((u'resource_set', u'filetype'),)", 'object_name': 'BootResourceFile'},
171+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
172+ 'extra': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
173+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
174+ 'filetype': ('django.db.models.fields.CharField', [], {'default': "u'tgz'", 'max_length': '20'}),
175+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
176+ 'largefile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.LargeFile']"}),
177+ 'resource_set': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'files'", 'to': u"orm['maasserver.BootResourceSet']"}),
178+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
179+ },
180+ u'maasserver.bootresourceset': {
181+ 'Meta': {'unique_together': "((u'resource', u'version'),)", 'object_name': 'BootResourceSet'},
182+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
183+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
184+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
185+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'sets'", 'to': u"orm['maasserver.BootResource']"}),
186+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
187+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '255'})
188+ },
189+ u'maasserver.bootsource': {
190+ 'Meta': {'object_name': 'BootSource'},
191+ 'cluster': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}),
192+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
193+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
194+ 'keyring_data': ('maasserver.fields.EditableBinaryField', [], {'blank': 'True'}),
195+ 'keyring_filename': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
196+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
197+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
198+ },
199+ u'maasserver.bootsourceselection': {
200+ 'Meta': {'object_name': 'BootSourceSelection'},
201+ 'arches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
202+ 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}),
203+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
204+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
205+ 'labels': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
206+ 'release': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
207+ 'subarches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
208+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
209+ },
210+ u'maasserver.componenterror': {
211+ 'Meta': {'object_name': 'ComponentError'},
212+ 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}),
213+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
214+ 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
215+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
216+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
217+ },
218+ u'maasserver.config': {
219+ 'Meta': {'object_name': 'Config'},
220+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
221+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
222+ 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'})
223+ },
224+ u'maasserver.dhcplease': {
225+ 'Meta': {'object_name': 'DHCPLease'},
226+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
227+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
228+ 'mac': ('maasserver.fields.MACAddressField', [], {}),
229+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"})
230+ },
231+ u'maasserver.downloadprogress': {
232+ 'Meta': {'object_name': 'DownloadProgress'},
233+ 'bytes_downloaded': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
234+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
235+ 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}),
236+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
237+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
238+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
239+ 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
240+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
241+ },
242+ u'maasserver.event': {
243+ 'Meta': {'object_name': 'Event'},
244+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
245+ 'description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}),
246+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
247+ 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}),
248+ 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.EventType']"}),
249+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
250+ },
251+ u'maasserver.eventtype': {
252+ 'Meta': {'object_name': 'EventType'},
253+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
254+ 'description': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
255+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
256+ 'level': ('django.db.models.fields.IntegerField', [], {}),
257+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
258+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
259+ },
260+ u'maasserver.filestorage': {
261+ 'Meta': {'unique_together': "((u'filename', u'owner'),)", 'object_name': 'FileStorage'},
262+ 'content': ('metadataserver.fields.BinaryField', [], {'blank': 'True'}),
263+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
264+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
265+ 'key': ('django.db.models.fields.CharField', [], {'default': "u'bf5ca3cc-21b2-11e4-9913-0026c71eea0e'", 'unique': 'True', 'max_length': '36'}),
266+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
267+ },
268+ u'maasserver.largefile': {
269+ 'Meta': {'object_name': 'LargeFile'},
270+ 'content': ('maasserver.fields.LargeObjectField', [], {}),
271+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
272+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
273+ 'sha256': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}),
274+ 'total_size': ('django.db.models.fields.BigIntegerField', [], {}),
275+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
276+ },
277+ u'maasserver.licensekey': {
278+ 'Meta': {'unique_together': "((u'osystem', u'distro_series'),)", 'object_name': 'LicenseKey'},
279+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
280+ 'distro_series': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
281+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
282+ 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
283+ 'osystem': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
284+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
285+ },
286+ u'maasserver.macaddress': {
287+ 'Meta': {'ordering': "(u'created',)", 'object_name': 'MACAddress'},
288+ 'cluster_interface': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['maasserver.NodeGroupInterface']", 'null': 'True', 'blank': 'True'}),
289+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
290+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
291+ 'ip_addresses': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.StaticIPAddress']", 'symmetrical': 'False', 'through': u"orm['maasserver.MACStaticIPAddressLink']", 'blank': 'True'}),
292+ 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}),
293+ 'networks': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Network']", 'symmetrical': 'False', 'blank': 'True'}),
294+ 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}),
295+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
296+ },
297+ u'maasserver.macstaticipaddresslink': {
298+ 'Meta': {'unique_together': "((u'ip_address', u'mac_address'),)", 'object_name': 'MACStaticIPAddressLink'},
299+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
300+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
301+ 'ip_address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.StaticIPAddress']", 'unique': 'True'}),
302+ 'mac_address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.MACAddress']"}),
303+ 'nic_alias': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
304+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
305+ },
306+ u'maasserver.network': {
307+ 'Meta': {'object_name': 'Network'},
308+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
309+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
310+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
311+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
312+ 'netmask': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39'}),
313+ 'vlan_tag': ('django.db.models.fields.PositiveSmallIntegerField', [], {'unique': 'True', 'null': 'True', 'blank': 'True'})
314+ },
315+ u'maasserver.node': {
316+ 'Meta': {'object_name': 'Node'},
317+ 'agent_name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
318+ 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '31'}),
319+ 'boot_type': ('django.db.models.fields.CharField', [], {'default': "u'fastpath'", 'max_length': '20'}),
320+ 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
321+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
322+ 'distro_series': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
323+ 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
324+ 'error_description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}),
325+ 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}),
326+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
327+ 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}),
328+ 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
329+ 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
330+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}),
331+ 'osystem': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
332+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
333+ 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
334+ 'power_state': ('django.db.models.fields.CharField', [], {'default': "u'unknown'", 'max_length': '10'}),
335+ 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}),
336+ 'routers': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'macaddr'", 'null': 'True', 'blank': 'True'}),
337+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}),
338+ 'storage': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
339+ 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-bf56dba4-21b2-11e4-9913-0026c71eea0e'", 'unique': 'True', 'max_length': '41'}),
340+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}),
341+ 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'null': 'True'}),
342+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
343+ 'zone': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Zone']", 'on_delete': 'models.SET_DEFAULT'})
344+ },
345+ u'maasserver.nodegroup': {
346+ 'Meta': {'object_name': 'NodeGroup'},
347+ 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}),
348+ 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'unique': 'True'}),
349+ 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}),
350+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
351+ 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
352+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
353+ 'maas_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
354+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
355+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
356+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
357+ 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
358+ },
359+ u'maasserver.nodegroupinterface': {
360+ 'Meta': {'unique_together': "((u'nodegroup', u'name'),)", 'object_name': 'NodeGroupInterface'},
361+ 'broadcast_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
362+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
363+ 'foreign_dhcp_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
364+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
365+ 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
366+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39'}),
367+ 'ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
368+ 'ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
369+ 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
370+ 'name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
371+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
372+ 'router_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
373+ 'static_ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
374+ 'static_ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
375+ 'subnet_mask': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
376+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
377+ },
378+ u'maasserver.sshkey': {
379+ 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'},
380+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
381+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
382+ 'key': ('django.db.models.fields.TextField', [], {}),
383+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
384+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
385+ },
386+ u'maasserver.sslkey': {
387+ 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSLKey'},
388+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
389+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
390+ 'key': ('django.db.models.fields.TextField', [], {}),
391+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
392+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
393+ },
394+ u'maasserver.staticipaddress': {
395+ 'Meta': {'object_name': 'StaticIPAddress'},
396+ 'alloc_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
397+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
398+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
399+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
400+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
401+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
402+ },
403+ u'maasserver.tag': {
404+ 'Meta': {'object_name': 'Tag'},
405+ 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
406+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
407+ 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
408+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
409+ 'kernel_opts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
410+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
411+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
412+ },
413+ u'maasserver.userprofile': {
414+ 'Meta': {'object_name': 'UserProfile'},
415+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
416+ 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'})
417+ },
418+ u'maasserver.zone': {
419+ 'Meta': {'ordering': "[u'name']", 'object_name': 'Zone'},
420+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
421+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
422+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
423+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
424+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
425+ },
426+ u'piston.consumer': {
427+ 'Meta': {'object_name': 'Consumer'},
428+ 'description': ('django.db.models.fields.TextField', [], {}),
429+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
430+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
431+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
432+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
433+ 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}),
434+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': u"orm['auth.User']"})
435+ },
436+ u'piston.token': {
437+ 'Meta': {'object_name': 'Token'},
438+ 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
439+ 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
440+ 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Consumer']"}),
441+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
442+ 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
443+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
444+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
445+ 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1407801231L'}),
446+ 'token_type': ('django.db.models.fields.IntegerField', [], {}),
447+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': u"orm['auth.User']"}),
448+ 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'})
449+ }
450+ }
451+
452+ complete_apps = ['maasserver']
453+ symmetrical = True