Merge lp:~allenap/maas/too-many-physical-interfaces--bug-1485584 into lp:~maas-committers/maas/trunk

Proposed by Gavin Panella
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: 4201
Proposed branch: lp:~allenap/maas/too-many-physical-interfaces--bug-1485584
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 547 lines (+524/-1)
2 files modified
src/maasserver/migrations/0163_unique_mac_and_name_for_interface.py (+516/-0)
src/maasserver/models/tests/test_interface.py (+8/-1)
To merge this branch: bzr merge lp:~allenap/maas/too-many-physical-interfaces--bug-1485584
Reviewer Review Type Date Requested Status
Blake Rouse (community) Approve
Review via email: mp+268234@code.launchpad.net

Commit message

Forbid multiple interfaces with the same MAC and name, where a MAC is specified.

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

Includes a test at the end of the diff; don't miss it!

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

After looking into I think its best to allow the MAC address to be NULL for VLANInterfaces. We can get the MACAddress by looking at the parent.

This should land you need to adjust the numbering to follow my branch here: https://code.launchpad.net/~blake-rouse/maas/fix-1485584/+merge/268261

Just re-number the migration to 0163.

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

Thanks Blake! Renumbered.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'src/maasserver/migrations/0163_unique_mac_and_name_for_interface.py'
2--- src/maasserver/migrations/0163_unique_mac_and_name_for_interface.py 1970-01-01 00:00:00 +0000
3+++ src/maasserver/migrations/0163_unique_mac_and_name_for_interface.py 2015-08-18 10:13:20 +0000
4@@ -0,0 +1,516 @@
5+# -*- coding: utf-8 -*-
6+
7+from django.db import models
8+from south.db import db
9+from south.utils import datetime_utils as datetime
10+from south.v2 import SchemaMigration
11+
12+
13+class Migration(SchemaMigration):
14+
15+ def forwards(self, orm):
16+ db.execute(
17+ "CREATE UNIQUE INDEX maasserver_interface_mac_id_name_idx ON "
18+ "maasserver_interface (mac_id, name) WHERE mac_id IS NOT NULL")
19+
20+ def backwards(self, orm):
21+ db.execute("DROP INDEX maasserver_interface_mac_id_name_idx")
22+
23+ models = {
24+ u'auth.group': {
25+ 'Meta': {'object_name': 'Group'},
26+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
27+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
28+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
29+ },
30+ u'auth.permission': {
31+ 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
32+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
33+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
34+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
35+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
36+ },
37+ u'auth.user': {
38+ 'Meta': {'object_name': 'User'},
39+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
40+ 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}),
41+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
42+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
43+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
44+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
45+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
46+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
47+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
48+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
49+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
50+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
51+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
52+ },
53+ u'contenttypes.contenttype': {
54+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
55+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
56+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
57+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
58+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
59+ },
60+ u'maasserver.blockdevice': {
61+ 'Meta': {'ordering': "[u'id']", 'unique_together': "((u'node', u'name'),)", 'object_name': 'BlockDevice'},
62+ 'block_size': ('django.db.models.fields.IntegerField', [], {}),
63+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
64+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
65+ 'id_path': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
66+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
67+ 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}),
68+ 'size': ('django.db.models.fields.BigIntegerField', [], {}),
69+ 'tags': ('djorm_pgarray.fields.ArrayField', [], {'default': '[]', 'dbtype': "u'text'", 'blank': 'True'}),
70+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
71+ },
72+ u'maasserver.bootresource': {
73+ 'Meta': {'unique_together': "((u'name', u'architecture'),)", 'object_name': 'BootResource'},
74+ 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
75+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
76+ 'extra': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
77+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
78+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
79+ 'rtype': ('django.db.models.fields.IntegerField', [], {'max_length': '10'}),
80+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
81+ },
82+ u'maasserver.bootresourcefile': {
83+ 'Meta': {'unique_together': "((u'resource_set', u'filetype'),)", 'object_name': 'BootResourceFile'},
84+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
85+ 'extra': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
86+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
87+ 'filetype': ('django.db.models.fields.CharField', [], {'default': "u'root-tgz'", 'max_length': '20'}),
88+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
89+ 'largefile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.LargeFile']"}),
90+ 'resource_set': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'files'", 'to': u"orm['maasserver.BootResourceSet']"}),
91+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
92+ },
93+ u'maasserver.bootresourceset': {
94+ 'Meta': {'unique_together': "((u'resource', u'version'),)", 'object_name': 'BootResourceSet'},
95+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
96+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
97+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
98+ 'resource': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'sets'", 'to': u"orm['maasserver.BootResource']"}),
99+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
100+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '255'})
101+ },
102+ u'maasserver.bootsource': {
103+ 'Meta': {'object_name': 'BootSource'},
104+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
105+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
106+ 'keyring_data': ('maasserver.fields.EditableBinaryField', [], {'blank': 'True'}),
107+ 'keyring_filename': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
108+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
109+ 'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200'})
110+ },
111+ u'maasserver.bootsourcecache': {
112+ 'Meta': {'object_name': 'BootSourceCache'},
113+ 'arch': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
114+ 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}),
115+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
116+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
117+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
118+ 'os': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
119+ 'release': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
120+ 'subarch': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
121+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
122+ },
123+ u'maasserver.bootsourceselection': {
124+ 'Meta': {'unique_together': "((u'boot_source', u'os', u'release'),)", 'object_name': 'BootSourceSelection'},
125+ 'arches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
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+ 'labels': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
130+ 'os': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
131+ 'release': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
132+ 'subarches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
133+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
134+ },
135+ u'maasserver.cacheset': {
136+ 'Meta': {'object_name': 'CacheSet'},
137+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
138+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
139+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
140+ },
141+ u'maasserver.candidatename': {
142+ 'Meta': {'unique_together': "((u'name', u'position'),)", 'object_name': 'CandidateName'},
143+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
144+ 'name': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
145+ 'position': ('django.db.models.fields.IntegerField', [], {})
146+ },
147+ u'maasserver.componenterror': {
148+ 'Meta': {'object_name': 'ComponentError'},
149+ 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}),
150+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
151+ 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
152+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
153+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
154+ },
155+ u'maasserver.config': {
156+ 'Meta': {'object_name': 'Config'},
157+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
158+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
159+ 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'})
160+ },
161+ u'maasserver.dhcplease': {
162+ 'Meta': {'object_name': 'DHCPLease'},
163+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
164+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
165+ 'mac': ('maasserver.fields.MACAddressField', [], {}),
166+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"})
167+ },
168+ u'maasserver.downloadprogress': {
169+ 'Meta': {'object_name': 'DownloadProgress'},
170+ 'bytes_downloaded': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
171+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
172+ 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}),
173+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
174+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
175+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
176+ 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
177+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
178+ },
179+ u'maasserver.event': {
180+ 'Meta': {'object_name': 'Event'},
181+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
182+ 'description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}),
183+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
184+ 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}),
185+ 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.EventType']"}),
186+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
187+ },
188+ u'maasserver.eventtype': {
189+ 'Meta': {'object_name': 'EventType'},
190+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
191+ 'description': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
192+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
193+ 'level': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
194+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
195+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
196+ },
197+ u'maasserver.fabric': {
198+ 'Meta': {'object_name': 'Fabric'},
199+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
200+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
201+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
202+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
203+ },
204+ u'maasserver.filestorage': {
205+ 'Meta': {'unique_together': "((u'filename', u'owner'),)", 'object_name': 'FileStorage'},
206+ 'content': ('metadataserver.fields.BinaryField', [], {'blank': 'True'}),
207+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
208+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
209+ 'key': ('django.db.models.fields.CharField', [], {'default': "u'73e2803e-4591-11e5-97cb-00163edfc3e6'", 'unique': 'True', 'max_length': '36'}),
210+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
211+ },
212+ u'maasserver.filesystem': {
213+ 'Meta': {'object_name': 'Filesystem'},
214+ 'block_device': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BlockDevice']", 'unique': 'True', 'null': 'True', 'blank': 'True'}),
215+ 'cache_set': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'filesystems'", 'null': 'True', 'to': u"orm['maasserver.CacheSet']"}),
216+ 'create_params': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
217+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
218+ 'filesystem_group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'filesystems'", 'null': 'True', 'to': u"orm['maasserver.FilesystemGroup']"}),
219+ 'fstype': ('django.db.models.fields.CharField', [], {'default': "u'ext4'", 'max_length': '20'}),
220+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
221+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
222+ 'mount_params': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
223+ 'mount_point': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
224+ 'partition': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Partition']", 'unique': 'True', 'null': 'True', 'blank': 'True'}),
225+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
226+ 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
227+ },
228+ u'maasserver.filesystemgroup': {
229+ 'Meta': {'object_name': 'FilesystemGroup'},
230+ 'cache_mode': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}),
231+ 'cache_set': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.CacheSet']", 'null': 'True', 'blank': 'True'}),
232+ 'create_params': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
233+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
234+ 'group_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
235+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
236+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
237+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
238+ 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
239+ },
240+ u'maasserver.interface': {
241+ 'Meta': {'ordering': "(u'created',)", 'object_name': 'Interface'},
242+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
243+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
244+ 'ip_addresses': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['maasserver.StaticIPAddress']", 'null': 'True', 'blank': 'True'}),
245+ 'ipv4_params': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
246+ 'ipv6_params': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
247+ 'mac': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.MACAddress']", 'null': 'True', 'blank': 'True'}),
248+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
249+ 'params': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
250+ 'parents': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['maasserver.Interface']", 'null': 'True', 'through': u"orm['maasserver.InterfaceRelationship']", 'blank': 'True'}),
251+ 'tags': ('djorm_pgarray.fields.ArrayField', [], {'default': '[]', 'dbtype': "u'text'", 'blank': 'True'}),
252+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
253+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
254+ 'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.VLAN']", 'on_delete': 'models.PROTECT'})
255+ },
256+ u'maasserver.interfacerelationship': {
257+ 'Meta': {'object_name': 'InterfaceRelationship'},
258+ 'child': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'parent_relationships'", 'to': u"orm['maasserver.Interface']"}),
259+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
260+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
261+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'children_relationships'", 'to': u"orm['maasserver.Interface']"}),
262+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
263+ },
264+ u'maasserver.largefile': {
265+ 'Meta': {'object_name': 'LargeFile'},
266+ 'content': ('maasserver.fields.LargeObjectField', [], {}),
267+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
268+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
269+ 'sha256': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}),
270+ 'total_size': ('django.db.models.fields.BigIntegerField', [], {}),
271+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
272+ },
273+ u'maasserver.licensekey': {
274+ 'Meta': {'unique_together': "((u'osystem', u'distro_series'),)", 'object_name': 'LicenseKey'},
275+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
276+ 'distro_series': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
277+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
278+ 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
279+ 'osystem': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
280+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
281+ },
282+ u'maasserver.macaddress': {
283+ 'Meta': {'ordering': "(u'created',)", 'object_name': 'MACAddress'},
284+ 'cluster_interface': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['maasserver.NodeGroupInterface']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
285+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
286+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
287+ 'ip_addresses': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.StaticIPAddress']", 'symmetrical': 'False', 'through': u"orm['maasserver.MACStaticIPAddressLink']", 'blank': 'True'}),
288+ 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}),
289+ 'networks': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Network']", 'symmetrical': 'False', 'blank': 'True'}),
290+ 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']", 'null': 'True', 'blank': 'True'}),
291+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
292+ },
293+ u'maasserver.macstaticipaddresslink': {
294+ 'Meta': {'unique_together': "((u'ip_address', u'mac_address'),)", 'object_name': 'MACStaticIPAddressLink'},
295+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
296+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
297+ 'ip_address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.StaticIPAddress']", 'unique': 'True'}),
298+ 'mac_address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.MACAddress']"}),
299+ 'nic_alias': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
300+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
301+ },
302+ u'maasserver.network': {
303+ 'Meta': {'object_name': 'Network'},
304+ 'default_gateway': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
305+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
306+ 'dns_servers': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
307+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
308+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
309+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
310+ 'netmask': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39'}),
311+ 'vlan_tag': ('django.db.models.fields.PositiveSmallIntegerField', [], {'unique': 'True', 'null': 'True', 'blank': 'True'})
312+ },
313+ u'maasserver.node': {
314+ 'Meta': {'object_name': 'Node'},
315+ 'agent_name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
316+ 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '31', 'null': 'True', 'blank': 'True'}),
317+ 'bios_boot_method': ('django.db.models.fields.CharField', [], {'max_length': '31', 'null': 'True', 'blank': 'True'}),
318+ 'boot_disk': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': u"orm['maasserver.PhysicalBlockDevice']", 'blank': 'True', 'null': 'True'}),
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+ 'disable_ipv4': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
323+ 'distro_series': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
324+ 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
325+ 'error_description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}),
326+ 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}),
327+ 'hwe_kernel': ('django.db.models.fields.CharField', [], {'max_length': '31', 'null': 'True', 'blank': 'True'}),
328+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
329+ 'installable': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
330+ 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}),
331+ 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
332+ 'min_hwe_kernel': ('django.db.models.fields.CharField', [], {'max_length': '31', 'null': 'True', 'blank': 'True'}),
333+ 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
334+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}),
335+ 'osystem': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
336+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
337+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "u'children'", 'null': 'True', 'blank': 'True', 'to': u"orm['maasserver.Node']"}),
338+ 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'max_length': '32768', 'blank': 'True'}),
339+ 'power_state': ('django.db.models.fields.CharField', [], {'default': "u'unknown'", 'max_length': '10'}),
340+ 'power_state_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
341+ 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}),
342+ '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'}),
343+ 'routers': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'macaddr'", 'null': 'True', 'blank': 'True'}),
344+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}),
345+ 'swap_size': ('django.db.models.fields.BigIntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
346+ 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-73de73ea-4591-11e5-97cb-00163edfc3e6'", 'unique': 'True', 'max_length': '41'}),
347+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}),
348+ 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'null': 'True'}),
349+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
350+ 'zone': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Zone']", 'on_delete': 'models.SET_DEFAULT'})
351+ },
352+ u'maasserver.nodegroup': {
353+ 'Meta': {'object_name': 'NodeGroup'},
354+ 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}),
355+ 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'unique': 'True'}),
356+ 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}),
357+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
358+ 'default_disable_ipv4': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
359+ 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
360+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
361+ 'maas_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
362+ 'name': ('maasserver.models.nodegroup.DomainNameField', [], {'max_length': '80', 'blank': 'True'}),
363+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
364+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
365+ 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
366+ },
367+ u'maasserver.nodegroupinterface': {
368+ 'Meta': {'unique_together': "((u'nodegroup', u'name'),)", 'object_name': 'NodeGroupInterface'},
369+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
370+ 'foreign_dhcp_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
371+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
372+ 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
373+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39'}),
374+ 'ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
375+ 'ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
376+ 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
377+ 'name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
378+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
379+ 'router_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
380+ 'static_ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
381+ 'static_ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
382+ 'subnet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Subnet']", 'null': 'True', 'on_delete': 'models.PROTECT', 'blank': 'True'}),
383+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
384+ 'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.VLAN']", 'on_delete': 'models.PROTECT'})
385+ },
386+ u'maasserver.partition': {
387+ 'Meta': {'object_name': 'Partition'},
388+ 'bootable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
389+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
390+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
391+ 'partition_table': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'partitions'", 'to': u"orm['maasserver.PartitionTable']"}),
392+ 'size': ('django.db.models.fields.BigIntegerField', [], {}),
393+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
394+ 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '36', 'unique': 'True', 'null': 'True', 'blank': 'True'})
395+ },
396+ u'maasserver.partitiontable': {
397+ 'Meta': {'object_name': 'PartitionTable'},
398+ 'block_device': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BlockDevice']"}),
399+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
400+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
401+ 'table_type': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '20'}),
402+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
403+ },
404+ u'maasserver.physicalblockdevice': {
405+ 'Meta': {'ordering': "[u'id']", 'object_name': 'PhysicalBlockDevice', '_ormbases': [u'maasserver.BlockDevice']},
406+ u'blockdevice_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['maasserver.BlockDevice']", 'unique': 'True', 'primary_key': 'True'}),
407+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
408+ 'serial': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
409+ },
410+ u'maasserver.space': {
411+ 'Meta': {'object_name': 'Space'},
412+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
413+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
414+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
415+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
416+ },
417+ u'maasserver.sshkey': {
418+ 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'},
419+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
420+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
421+ 'key': ('django.db.models.fields.TextField', [], {}),
422+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
423+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
424+ },
425+ u'maasserver.sslkey': {
426+ 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSLKey'},
427+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
428+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
429+ 'key': ('django.db.models.fields.TextField', [], {}),
430+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
431+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
432+ },
433+ u'maasserver.staticipaddress': {
434+ 'Meta': {'object_name': 'StaticIPAddress'},
435+ 'alloc_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
436+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
437+ 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
438+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
439+ 'ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
440+ 'subnet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Subnet']", 'null': 'True', 'blank': 'True'}),
441+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
442+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
443+ },
444+ u'maasserver.subnet': {
445+ 'Meta': {'unique_together': "((u'name', u'space'),)", 'object_name': 'Subnet'},
446+ 'cidr': ('maasserver.fields.CIDRField', [], {'unique': 'True'}),
447+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
448+ 'dns_servers': ('djorm_pgarray.fields.ArrayField', [], {'default': '[]', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
449+ 'gateway_ip': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
450+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
451+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
452+ 'space': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Space']", 'on_delete': 'models.PROTECT'}),
453+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
454+ 'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.VLAN']", 'on_delete': 'models.PROTECT'})
455+ },
456+ u'maasserver.tag': {
457+ 'Meta': {'object_name': 'Tag'},
458+ 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
459+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
460+ 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
461+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
462+ 'kernel_opts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
463+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
464+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
465+ },
466+ u'maasserver.userprofile': {
467+ 'Meta': {'object_name': 'UserProfile'},
468+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
469+ 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'})
470+ },
471+ u'maasserver.virtualblockdevice': {
472+ 'Meta': {'ordering': "[u'id']", 'object_name': 'VirtualBlockDevice', '_ormbases': [u'maasserver.BlockDevice']},
473+ u'blockdevice_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['maasserver.BlockDevice']", 'unique': 'True', 'primary_key': 'True'}),
474+ 'filesystem_group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'virtual_devices'", 'to': u"orm['maasserver.FilesystemGroup']"}),
475+ 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
476+ },
477+ u'maasserver.vlan': {
478+ 'Meta': {'unique_together': "((u'vid', u'fabric'), (u'name', u'fabric'))", 'object_name': 'VLAN'},
479+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
480+ 'fabric': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Fabric']"}),
481+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
482+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
483+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
484+ 'vid': ('django.db.models.fields.IntegerField', [], {})
485+ },
486+ u'maasserver.zone': {
487+ 'Meta': {'ordering': "[u'name']", 'object_name': 'Zone'},
488+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
489+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
490+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
491+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
492+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
493+ },
494+ u'piston.consumer': {
495+ 'Meta': {'object_name': 'Consumer'},
496+ 'description': ('django.db.models.fields.TextField', [], {}),
497+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
498+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
499+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
500+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
501+ 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}),
502+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': u"orm['auth.User']"})
503+ },
504+ u'piston.token': {
505+ 'Meta': {'object_name': 'Token'},
506+ 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
507+ 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
508+ 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Consumer']"}),
509+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
510+ 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
511+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
512+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
513+ 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1439892671L'}),
514+ 'token_type': ('django.db.models.fields.IntegerField', [], {}),
515+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': u"orm['auth.User']"}),
516+ 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'})
517+ }
518+ }
519+
520+ complete_apps = ['maasserver']
521
522=== modified file 'src/maasserver/models/tests/test_interface.py'
523--- src/maasserver/models/tests/test_interface.py 2015-08-13 07:42:50 +0000
524+++ src/maasserver/models/tests/test_interface.py 2015-08-18 10:13:20 +0000
525@@ -14,7 +14,7 @@
526 __metaclass__ = type
527 __all__ = []
528
529-
530+from django.db import IntegrityError
531 from maasserver.enum import (
532 INTERFACE_TYPE,
533 IPADDRESS_TYPE,
534@@ -52,6 +52,13 @@
535 self.assertThat(interface, MatchesStructure.byEquality(
536 mac=mac, name=name, type=INTERFACE_TYPE.PHYSICAL))
537
538+ def test_cannot_create_multiple_with_the_same_mac_and_name(self):
539+ name = factory.make_name("eth")
540+ mac = factory.make_MACAddress()
541+ PhysicalInterface(mac=mac, name=name).save()
542+ self.assertRaises(
543+ IntegrityError, PhysicalInterface(mac=mac, name=name).save)
544+
545 def test_unicode_representation_contains_essential_data(self):
546 name = factory.make_name('name')
547 mac = factory.make_MACAddress_with_Node()