Merge lp:~blake-rouse/maas/fix-vlan-to-untagged into lp:~maas-committers/maas/trunk
- fix-vlan-to-untagged
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Blake Rouse |
Approved revision: | no longer in the source branch. |
Merged at revision: | 4370 |
Proposed branch: | lp:~blake-rouse/maas/fix-vlan-to-untagged |
Merge into: | lp:~maas-committers/maas/trunk |
Diff against target: |
1262 lines (+742/-63) 26 files modified
src/maasserver/api/fabrics.py (+5/-0) src/maasserver/api/tests/test_fabrics.py (+1/-11) src/maasserver/api/tests/test_vlans.py (+3/-4) src/maasserver/api/vlans.py (+6/-1) src/maasserver/context_processors.py (+1/-0) src/maasserver/fixtures/dev_fixture.yaml (+4/-4) src/maasserver/forms_vlan.py (+8/-0) src/maasserver/migrations/0183_remove_required_name_from_vlan_and_fabric.py (+519/-0) src/maasserver/models/fabric.py (+8/-1) src/maasserver/models/tests/test_fabric.py (+9/-0) src/maasserver/models/tests/test_vlan.py (+9/-0) src/maasserver/models/vlan.py (+10/-4) src/maasserver/static/js/angular/controllers/node_details_networking.js (+28/-3) src/maasserver/static/js/angular/controllers/subnets_list.js (+3/-1) src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js (+58/-11) src/maasserver/static/js/angular/filters/remove_default_vlan.js (+17/-0) src/maasserver/static/js/angular/filters/tests/test_remove_default_vlan.js (+30/-0) src/maasserver/static/partials/node-details.html (+6/-6) src/maasserver/testing/factory.py (+0/-4) src/maasserver/tests/test_forms_fabric.py (+0/-7) src/maasserver/tests/test_forms_space.py (+2/-2) src/maasserver/tests/test_forms_vlan.py (+11/-2) src/maasserver/websockets/handlers/fabric.py (+1/-0) src/maasserver/websockets/handlers/tests/test_fabric.py (+1/-1) src/maasserver/websockets/handlers/tests/test_vlan.py (+1/-1) src/maasserver/websockets/handlers/vlan.py (+1/-0) |
To merge this branch: | bzr merge lp:~blake-rouse/maas/fix-vlan-to-untagged |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Blake Rouse (community) | Approve | ||
Mike Pontillo (community) | Approve | ||
Review via email: mp+273948@code.launchpad.net |
Commit message
Remove the requirement of names on fabric and vlan. Fix fabric name and vlan name based on the id and vid respectively. Fix the UI to not allow the VLAN editing on physical interfaces. Fix other small UI issues with node networking. Prevent the default VLAN from being editable on a fabric.
Description of the change
Blake Rouse (blake-rouse) wrote : | # |
Thanks for the review. I added it to the commit message.
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~blake-rouse/maas/fix-vlan-to-untagged into lp:maas failed. Below is the output from the failed tests.
Hit http://
Ign http://
Get:1 http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Get:2 http://
Get:3 http://
Get:4 http://
Get:5 http://
Get:6 http://
Get:7 http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Ign http://
Ign http://
Fetched 1,868 kB in 4s (466 kB/s)
Reading package lists...
sudo DEBIAN_
--
Preview Diff
1 | === modified file 'src/maasserver/api/fabrics.py' |
2 | --- src/maasserver/api/fabrics.py 2015-08-18 15:10:54 +0000 |
3 | +++ src/maasserver/api/fabrics.py 2015-10-09 21:26:30 +0000 |
4 | @@ -75,6 +75,11 @@ |
5 | return ('fabric_handler', (fabric_id,)) |
6 | |
7 | @classmethod |
8 | + def name(cls, fabric): |
9 | + """Return the name of the fabric.""" |
10 | + return fabric.get_name() |
11 | + |
12 | + @classmethod |
13 | def vlans(cls, fabric): |
14 | """Return VLANs within the specified fabric.""" |
15 | return fabric.vlan_set.all() |
16 | |
17 | === modified file 'src/maasserver/api/tests/test_fabrics.py' |
18 | --- src/maasserver/api/tests/test_fabrics.py 2015-08-13 19:29:08 +0000 |
19 | +++ src/maasserver/api/tests/test_fabrics.py 2015-10-09 21:26:30 +0000 |
20 | @@ -82,16 +82,6 @@ |
21 | self.assertEqual( |
22 | httplib.FORBIDDEN, response.status_code, response.content) |
23 | |
24 | - def test_create_requires_name(self): |
25 | - self.become_admin() |
26 | - uri = get_fabrics_uri() |
27 | - response = self.client.post(uri, {}) |
28 | - self.assertEqual( |
29 | - httplib.BAD_REQUEST, response.status_code, response.content) |
30 | - self.assertEqual({ |
31 | - "name": ["This field is required."], |
32 | - }, json.loads(response.content)) |
33 | - |
34 | |
35 | class TestFabricAPI(APITestCase): |
36 | |
37 | @@ -112,7 +102,7 @@ |
38 | parsed_fabric = json.loads(response.content) |
39 | self.assertThat(parsed_fabric, ContainsDict({ |
40 | "id": Equals(fabric.id), |
41 | - "name": Equals(fabric.name), |
42 | + "name": Equals(fabric.get_name()), |
43 | })) |
44 | self.assertItemsEqual([ |
45 | vlan.id |
46 | |
47 | === modified file 'src/maasserver/api/tests/test_vlans.py' |
48 | --- src/maasserver/api/tests/test_vlans.py 2015-08-15 00:08:54 +0000 |
49 | +++ src/maasserver/api/tests/test_vlans.py 2015-10-09 21:26:30 +0000 |
50 | @@ -93,7 +93,7 @@ |
51 | self.assertEqual( |
52 | httplib.FORBIDDEN, response.status_code, response.content) |
53 | |
54 | - def test_create_requires_name_and_vid(self): |
55 | + def test_create_requires_vid(self): |
56 | self.become_admin() |
57 | fabric = factory.make_Fabric() |
58 | uri = get_vlans_uri(fabric) |
59 | @@ -101,7 +101,6 @@ |
60 | self.assertEqual( |
61 | httplib.BAD_REQUEST, response.status_code, response.content) |
62 | self.assertEqual({ |
63 | - "name": ["This field is required."], |
64 | "vid": [ |
65 | "This field is required.", |
66 | "Vid must be between 0 and 4095."], |
67 | @@ -128,9 +127,9 @@ |
68 | parsed_vlan = json.loads(response.content) |
69 | self.assertThat(parsed_vlan, ContainsDict({ |
70 | "id": Equals(vlan.id), |
71 | - "name": Equals(vlan.name), |
72 | + "name": Equals(vlan.get_name()), |
73 | "vid": Equals(vlan.vid), |
74 | - "fabric": Equals(fabric.name), |
75 | + "fabric": Equals(fabric.get_name()), |
76 | "resource_uri": Equals(get_vlan_uri(vlan)), |
77 | })) |
78 | |
79 | |
80 | === modified file 'src/maasserver/api/vlans.py' |
81 | --- src/maasserver/api/vlans.py 2015-08-18 15:25:03 +0000 |
82 | +++ src/maasserver/api/vlans.py 2015-10-09 21:26:30 +0000 |
83 | @@ -90,7 +90,12 @@ |
84 | @classmethod |
85 | def fabric(cls, vlan): |
86 | """Return fabric name.""" |
87 | - return vlan.fabric.name |
88 | + return vlan.fabric.get_name() |
89 | + |
90 | + @classmethod |
91 | + def name(cls, vlan): |
92 | + """Return the VLAN name.""" |
93 | + return vlan.get_name() |
94 | |
95 | def read(self, request, fabric_id, vlan_id): |
96 | """Read VLAN on fabric. |
97 | |
98 | === modified file 'src/maasserver/context_processors.py' |
99 | --- src/maasserver/context_processors.py 2015-09-30 22:30:55 +0000 |
100 | +++ src/maasserver/context_processors.py 2015-10-09 21:26:30 +0000 |
101 | @@ -76,6 +76,7 @@ |
102 | 'js/angular/filters/by_fabric.js', |
103 | 'js/angular/filters/by_vlan.js', |
104 | 'js/angular/filters/by_space.js', |
105 | + 'js/angular/filters/remove_default_vlan.js', |
106 | 'js/angular/controllers/error.js', |
107 | 'js/angular/controllers/nodes_list.js', |
108 | 'js/angular/controllers/add_hardware.js', |
109 | |
110 | === modified file 'src/maasserver/fixtures/dev_fixture.yaml' |
111 | --- src/maasserver/fixtures/dev_fixture.yaml 2015-09-27 20:01:05 +0000 |
112 | +++ src/maasserver/fixtures/dev_fixture.yaml 2015-10-09 21:26:30 +0000 |
113 | @@ -9484,11 +9484,11 @@ |
114 | updated: ! '2015-09-18 15:38:26.981744'} |
115 | model: maasserver.event |
116 | pk: 236 |
117 | -- fields: {created: ! '2015-09-18 15:14:45.727015', name: Default fabric, updated: ! '2015-09-18 |
118 | +- fields: {created: ! '2015-09-18 15:14:45.727015', updated: ! '2015-09-18 |
119 | 15:14:45.727015'} |
120 | model: maasserver.fabric |
121 | pk: 0 |
122 | -- fields: {created: ! '2015-09-18 15:14:45.727015', name: Extra fabric, updated: ! '2015-09-18 |
123 | +- fields: {created: ! '2015-09-18 15:14:45.727015', updated: ! '2015-09-18 |
124 | 15:14:45.727015'} |
125 | model: maasserver.fabric |
126 | pk: 1 |
127 | @@ -9512,11 +9512,11 @@ |
128 | - fields: {filesystem_group: 4, uuid: 3a140acf-6a98-4572-aa2f-3de2ba56b818} |
129 | model: maasserver.virtualblockdevice |
130 | pk: 12 |
131 | -- fields: {created: ! '2015-09-18 15:14:45.727015', fabric: 0, name: Default VLAN, |
132 | +- fields: {created: ! '2015-09-18 15:14:45.727015', fabric: 0, |
133 | updated: ! '2015-09-18 15:14:45.727015', vid: 0} |
134 | model: maasserver.vlan |
135 | pk: 0 |
136 | -- fields: {created: ! '2015-09-18 15:14:45.727015', fabric: 1, name: Extra VLAN, |
137 | +- fields: {created: ! '2015-09-18 15:14:45.727015', fabric: 1, |
138 | updated: ! '2015-09-18 15:14:45.727015', vid: 0} |
139 | model: maasserver.vlan |
140 | pk: 1 |
141 | |
142 | === modified file 'src/maasserver/forms_vlan.py' |
143 | --- src/maasserver/forms_vlan.py 2015-08-14 14:00:46 +0000 |
144 | +++ src/maasserver/forms_vlan.py 2015-10-09 21:26:30 +0000 |
145 | @@ -16,6 +16,7 @@ |
146 | "VLANForm", |
147 | ] |
148 | |
149 | +from django.core.exceptions import ValidationError |
150 | from maasserver.forms import MAASModelForm |
151 | from maasserver.models.vlan import VLAN |
152 | |
153 | @@ -37,6 +38,13 @@ |
154 | if instance is None and self.fabric is None: |
155 | raise ValueError("Form requires either a instance or a fabric.") |
156 | |
157 | + def clean(self): |
158 | + cleaned_data = super(VLANForm, self).clean() |
159 | + if self.instance.id is not None and self.instance.is_fabric_default(): |
160 | + raise ValidationError( |
161 | + "Cannot modify the default VLAN for a fabric.") |
162 | + return cleaned_data |
163 | + |
164 | def save(self): |
165 | """Persist the interface into the database.""" |
166 | interface = super(VLANForm, self).save(commit=False) |
167 | |
168 | === added file 'src/maasserver/migrations/0183_remove_required_name_from_vlan_and_fabric.py' |
169 | --- src/maasserver/migrations/0183_remove_required_name_from_vlan_and_fabric.py 1970-01-01 00:00:00 +0000 |
170 | +++ src/maasserver/migrations/0183_remove_required_name_from_vlan_and_fabric.py 2015-10-09 21:26:30 +0000 |
171 | @@ -0,0 +1,519 @@ |
172 | +from django.db import models |
173 | +from south.db import db |
174 | +# -*- coding: utf-8 -*- |
175 | +from south.utils import datetime_utils as datetime |
176 | +from south.v2 import SchemaMigration |
177 | + |
178 | + |
179 | +class Migration(SchemaMigration): |
180 | + |
181 | + def forwards(self, orm): |
182 | + # Removing unique constraint on 'Fabric', fields ['name'] |
183 | + db.delete_unique(u'maasserver_fabric', ['name']) |
184 | + |
185 | + # Removing unique constraint on 'VLAN', fields ['name', 'fabric'] |
186 | + db.delete_unique(u'maasserver_vlan', ['name', 'fabric_id']) |
187 | + |
188 | + |
189 | + # Changing field 'VLAN.name' |
190 | + db.alter_column(u'maasserver_vlan', 'name', self.gf('django.db.models.fields.CharField')(max_length=256, null=True)) |
191 | + |
192 | + # Changing field 'Fabric.name' |
193 | + db.alter_column(u'maasserver_fabric', 'name', self.gf('django.db.models.fields.CharField')(max_length=256, null=True)) |
194 | + |
195 | + def backwards(self, orm): |
196 | + |
197 | + # User chose to not deal with backwards NULL issues for 'VLAN.name' |
198 | + raise RuntimeError("Cannot reverse this migration. 'VLAN.name' and its values cannot be restored.") |
199 | + # Adding unique constraint on 'VLAN', fields ['name', 'fabric'] |
200 | + db.create_unique(u'maasserver_vlan', ['name', 'fabric_id']) |
201 | + |
202 | + |
203 | + # User chose to not deal with backwards NULL issues for 'Fabric.name' |
204 | + raise RuntimeError("Cannot reverse this migration. 'Fabric.name' and its values cannot be restored.") |
205 | + # Adding unique constraint on 'Fabric', fields ['name'] |
206 | + db.create_unique(u'maasserver_fabric', ['name']) |
207 | + |
208 | + |
209 | + models = { |
210 | + u'auth.group': { |
211 | + 'Meta': {'object_name': 'Group'}, |
212 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
213 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
214 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
215 | + }, |
216 | + u'auth.permission': { |
217 | + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, |
218 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
219 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), |
220 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
221 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
222 | + }, |
223 | + u'auth.user': { |
224 | + 'Meta': {'object_name': 'User'}, |
225 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
226 | + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}), |
227 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
228 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), |
229 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
230 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
231 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
232 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
233 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
234 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
235 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
236 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), |
237 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
238 | + }, |
239 | + u'contenttypes.contenttype': { |
240 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
241 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
242 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
243 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
244 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
245 | + }, |
246 | + u'maasserver.blockdevice': { |
247 | + 'Meta': {'ordering': "[u'id']", 'unique_together': "((u'node', u'name'),)", 'object_name': 'BlockDevice'}, |
248 | + 'block_size': ('django.db.models.fields.IntegerField', [], {}), |
249 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
250 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
251 | + 'id_path': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), |
252 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
253 | + 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}), |
254 | + 'size': ('django.db.models.fields.BigIntegerField', [], {}), |
255 | + 'tags': ('djorm_pgarray.fields.ArrayField', [], {'default': '[]', 'dbtype': "u'text'", 'blank': 'True'}), |
256 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
257 | + }, |
258 | + u'maasserver.bootresource': { |
259 | + 'Meta': {'unique_together': "((u'name', u'architecture'),)", 'object_name': 'BootResource'}, |
260 | + 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
261 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
262 | + 'extra': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}), |
263 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
264 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
265 | + 'rtype': ('django.db.models.fields.IntegerField', [], {'max_length': '10'}), |
266 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
267 | + }, |
268 | + u'maasserver.bootresourcefile': { |
269 | + 'Meta': {'unique_together': "((u'resource_set', u'filetype'),)", 'object_name': 'BootResourceFile'}, |
270 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
271 | + 'extra': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}), |
272 | + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
273 | + 'filetype': ('django.db.models.fields.CharField', [], {'default': "u'root-tgz'", 'max_length': '20'}), |
274 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
275 | + 'largefile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.LargeFile']"}), |
276 | + 'resource_set': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'files'", 'to': u"orm['maasserver.BootResourceSet']"}), |
277 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
278 | + }, |
279 | + u'maasserver.bootresourceset': { |
280 | + 'Meta': {'unique_together': "((u'resource', u'version'),)", 'object_name': 'BootResourceSet'}, |
281 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
282 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
283 | + 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
284 | + 'resource': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'sets'", 'to': u"orm['maasserver.BootResource']"}), |
285 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
286 | + 'version': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
287 | + }, |
288 | + u'maasserver.bootsource': { |
289 | + 'Meta': {'object_name': 'BootSource'}, |
290 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
291 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
292 | + 'keyring_data': ('maasserver.fields.EditableBinaryField', [], {'blank': 'True'}), |
293 | + 'keyring_filename': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}), |
294 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
295 | + 'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200'}) |
296 | + }, |
297 | + u'maasserver.bootsourcecache': { |
298 | + 'Meta': {'object_name': 'BootSourceCache'}, |
299 | + 'arch': ('django.db.models.fields.CharField', [], {'max_length': '20'}), |
300 | + 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}), |
301 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
302 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
303 | + 'label': ('django.db.models.fields.CharField', [], {'max_length': '20'}), |
304 | + 'os': ('django.db.models.fields.CharField', [], {'max_length': '20'}), |
305 | + 'release': ('django.db.models.fields.CharField', [], {'max_length': '20'}), |
306 | + 'subarch': ('django.db.models.fields.CharField', [], {'max_length': '20'}), |
307 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
308 | + }, |
309 | + u'maasserver.bootsourceselection': { |
310 | + 'Meta': {'unique_together': "((u'boot_source', u'os', u'release'),)", 'object_name': 'BootSourceSelection'}, |
311 | + 'arches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}), |
312 | + 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}), |
313 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
314 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
315 | + 'labels': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}), |
316 | + 'os': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}), |
317 | + 'release': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}), |
318 | + 'subarches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}), |
319 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
320 | + }, |
321 | + u'maasserver.cacheset': { |
322 | + 'Meta': {'object_name': 'CacheSet'}, |
323 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
324 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
325 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
326 | + }, |
327 | + u'maasserver.candidatename': { |
328 | + 'Meta': {'unique_together': "((u'name', u'position'),)", 'object_name': 'CandidateName'}, |
329 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
330 | + 'name': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), |
331 | + 'position': ('django.db.models.fields.IntegerField', [], {}) |
332 | + }, |
333 | + u'maasserver.componenterror': { |
334 | + 'Meta': {'object_name': 'ComponentError'}, |
335 | + 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}), |
336 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
337 | + 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}), |
338 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
339 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
340 | + }, |
341 | + u'maasserver.config': { |
342 | + 'Meta': {'object_name': 'Config'}, |
343 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
344 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), |
345 | + 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'}) |
346 | + }, |
347 | + u'maasserver.downloadprogress': { |
348 | + 'Meta': {'object_name': 'DownloadProgress'}, |
349 | + 'bytes_downloaded': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
350 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
351 | + 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}), |
352 | + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
353 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
354 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
355 | + 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
356 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
357 | + }, |
358 | + u'maasserver.event': { |
359 | + 'Meta': {'object_name': 'Event'}, |
360 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
361 | + 'description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}), |
362 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
363 | + 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}), |
364 | + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.EventType']"}), |
365 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
366 | + }, |
367 | + u'maasserver.eventtype': { |
368 | + 'Meta': {'object_name': 'EventType'}, |
369 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
370 | + 'description': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
371 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
372 | + 'level': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), |
373 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), |
374 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
375 | + }, |
376 | + u'maasserver.fabric': { |
377 | + 'Meta': {'object_name': 'Fabric'}, |
378 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
379 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
380 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}), |
381 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
382 | + }, |
383 | + u'maasserver.fannetwork': { |
384 | + 'Meta': {'object_name': 'FanNetwork'}, |
385 | + 'bridge': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), |
386 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
387 | + 'dhcp': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), |
388 | + 'host_reserve': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}), |
389 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
390 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), |
391 | + 'off': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}), |
392 | + 'overlay': ('maasserver.fields.IPv4CIDRField', [], {'unique': 'True'}), |
393 | + 'underlay': ('maasserver.fields.IPv4CIDRField', [], {'unique': 'True'}), |
394 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
395 | + }, |
396 | + u'maasserver.filestorage': { |
397 | + 'Meta': {'unique_together': "((u'filename', u'owner'),)", 'object_name': 'FileStorage'}, |
398 | + 'content': ('metadataserver.fields.BinaryField', [], {'blank': 'True'}), |
399 | + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
400 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
401 | + 'key': ('django.db.models.fields.CharField', [], {'default': "u'a1d75a4c-6e44-11e5-a1dc-c48e8ffbb365'", 'unique': 'True', 'max_length': '36'}), |
402 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}) |
403 | + }, |
404 | + u'maasserver.filesystem': { |
405 | + 'Meta': {'unique_together': "((u'partition', u'acquired'), (u'block_device', u'acquired'))", 'object_name': 'Filesystem'}, |
406 | + 'acquired': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
407 | + 'block_device': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BlockDevice']", 'null': 'True', 'blank': 'True'}), |
408 | + 'cache_set': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'filesystems'", 'null': 'True', 'to': u"orm['maasserver.CacheSet']"}), |
409 | + 'create_params': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), |
410 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
411 | + 'filesystem_group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'filesystems'", 'null': 'True', 'to': u"orm['maasserver.FilesystemGroup']"}), |
412 | + 'fstype': ('django.db.models.fields.CharField', [], {'default': "u'ext4'", 'max_length': '20'}), |
413 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
414 | + 'label': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), |
415 | + 'mount_params': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), |
416 | + 'mount_point': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), |
417 | + 'partition': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Partition']", 'null': 'True', 'blank': 'True'}), |
418 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
419 | + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '36'}) |
420 | + }, |
421 | + u'maasserver.filesystemgroup': { |
422 | + 'Meta': {'object_name': 'FilesystemGroup'}, |
423 | + 'cache_mode': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}), |
424 | + 'cache_set': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.CacheSet']", 'null': 'True', 'blank': 'True'}), |
425 | + 'create_params': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), |
426 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
427 | + 'group_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), |
428 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
429 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
430 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
431 | + 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}) |
432 | + }, |
433 | + u'maasserver.interface': { |
434 | + 'Meta': {'ordering': "(u'created',)", 'object_name': 'Interface'}, |
435 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
436 | + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
437 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
438 | + 'ip_addresses': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['maasserver.StaticIPAddress']", 'null': 'True', 'blank': 'True'}), |
439 | + 'ipv4_params': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}), |
440 | + 'ipv6_params': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}), |
441 | + 'mac_address': ('maasserver.fields.MACAddressField', [], {'null': 'True', 'blank': 'True'}), |
442 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
443 | + 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']", 'null': 'True', 'blank': 'True'}), |
444 | + 'params': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}), |
445 | + 'parents': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['maasserver.Interface']", 'null': 'True', 'through': u"orm['maasserver.InterfaceRelationship']", 'blank': 'True'}), |
446 | + 'tags': ('djorm_pgarray.fields.ArrayField', [], {'default': '[]', 'dbtype': "u'text'", 'blank': 'True'}), |
447 | + 'type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), |
448 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
449 | + 'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.VLAN']", 'on_delete': 'models.PROTECT'}) |
450 | + }, |
451 | + u'maasserver.interfacerelationship': { |
452 | + 'Meta': {'object_name': 'InterfaceRelationship'}, |
453 | + 'child': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'parent_relationships'", 'to': u"orm['maasserver.Interface']"}), |
454 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
455 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
456 | + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'children_relationships'", 'to': u"orm['maasserver.Interface']"}), |
457 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
458 | + }, |
459 | + u'maasserver.largefile': { |
460 | + 'Meta': {'object_name': 'LargeFile'}, |
461 | + 'content': ('maasserver.fields.LargeObjectField', [], {}), |
462 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
463 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
464 | + 'sha256': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), |
465 | + 'total_size': ('django.db.models.fields.BigIntegerField', [], {}), |
466 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
467 | + }, |
468 | + u'maasserver.licensekey': { |
469 | + 'Meta': {'unique_together': "((u'osystem', u'distro_series'),)", 'object_name': 'LicenseKey'}, |
470 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
471 | + 'distro_series': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
472 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
473 | + 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
474 | + 'osystem': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
475 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
476 | + }, |
477 | + u'maasserver.node': { |
478 | + 'Meta': {'object_name': 'Node'}, |
479 | + 'agent_name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), |
480 | + 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '31', 'null': 'True', 'blank': 'True'}), |
481 | + 'bios_boot_method': ('django.db.models.fields.CharField', [], {'max_length': '31', 'null': 'True', 'blank': 'True'}), |
482 | + 'block_poweroff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
483 | + 'boot_cluster_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
484 | + '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'}), |
485 | + 'boot_interface': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': u"orm['maasserver.Interface']", 'blank': 'True', 'null': 'True'}), |
486 | + 'boot_type': ('django.db.models.fields.CharField', [], {'default': "u'fastpath'", 'max_length': '20'}), |
487 | + 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
488 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
489 | + 'disable_ipv4': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
490 | + 'distro_series': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}), |
491 | + 'enable_ssh': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
492 | + 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
493 | + 'error_description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}), |
494 | + 'gateway_link_ipv4': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': u"orm['maasserver.StaticIPAddress']", 'blank': 'True', 'null': 'True'}), |
495 | + 'gateway_link_ipv6': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': u"orm['maasserver.StaticIPAddress']", 'blank': 'True', 'null': 'True'}), |
496 | + 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}), |
497 | + 'hwe_kernel': ('django.db.models.fields.CharField', [], {'max_length': '31', 'null': 'True', 'blank': 'True'}), |
498 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
499 | + 'installable': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), |
500 | + 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), |
501 | + 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
502 | + 'min_hwe_kernel': ('django.db.models.fields.CharField', [], {'max_length': '31', 'null': 'True', 'blank': 'True'}), |
503 | + 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
504 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}), |
505 | + 'osystem': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}), |
506 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}), |
507 | + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "u'children'", 'null': 'True', 'blank': 'True', 'to': u"orm['maasserver.Node']"}), |
508 | + 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'max_length': '32768', 'blank': 'True'}), |
509 | + 'power_state': ('django.db.models.fields.CharField', [], {'default': "u'unknown'", 'max_length': '10'}), |
510 | + 'power_state_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), |
511 | + 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}), |
512 | + 'routers': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'macaddr'", 'null': 'True', 'blank': 'True'}), |
513 | + 'skip_networking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
514 | + 'skip_storage': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
515 | + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}), |
516 | + 'swap_size': ('django.db.models.fields.BigIntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}), |
517 | + 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-a1d55db4-6e44-11e5-a1dc-c48e8ffbb365'", 'unique': 'True', 'max_length': '41'}), |
518 | + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}), |
519 | + 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'null': 'True'}), |
520 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
521 | + 'zone': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Zone']", 'on_delete': 'models.SET_DEFAULT'}) |
522 | + }, |
523 | + u'maasserver.nodegroup': { |
524 | + 'Meta': {'object_name': 'NodeGroup'}, |
525 | + 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}), |
526 | + 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'unique': 'True'}), |
527 | + 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}), |
528 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
529 | + 'default_disable_ipv4': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
530 | + 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
531 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
532 | + 'maas_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
533 | + 'name': ('maasserver.models.nodegroup.DomainNameField', [], {'max_length': '80', 'blank': 'True'}), |
534 | + 'status': ('django.db.models.fields.IntegerField', [], {'default': '1'}), |
535 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
536 | + 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}) |
537 | + }, |
538 | + u'maasserver.nodegroupinterface': { |
539 | + 'Meta': {'unique_together': "((u'nodegroup', u'name'),)", 'object_name': 'NodeGroupInterface'}, |
540 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
541 | + 'foreign_dhcp_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
542 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
543 | + 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
544 | + 'ip': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39'}), |
545 | + 'ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
546 | + 'ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
547 | + 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
548 | + 'name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
549 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
550 | + 'static_ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
551 | + 'static_ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
552 | + 'subnet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Subnet']", 'null': 'True', 'on_delete': 'models.PROTECT', 'blank': 'True'}), |
553 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
554 | + 'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.VLAN']", 'on_delete': 'models.PROTECT'}) |
555 | + }, |
556 | + u'maasserver.partition': { |
557 | + 'Meta': {'object_name': 'Partition'}, |
558 | + 'bootable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
559 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
560 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
561 | + 'partition_table': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'partitions'", 'to': u"orm['maasserver.PartitionTable']"}), |
562 | + 'size': ('django.db.models.fields.BigIntegerField', [], {}), |
563 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
564 | + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '36', 'unique': 'True', 'null': 'True', 'blank': 'True'}) |
565 | + }, |
566 | + u'maasserver.partitiontable': { |
567 | + 'Meta': {'object_name': 'PartitionTable'}, |
568 | + 'block_device': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BlockDevice']"}), |
569 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
570 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
571 | + 'table_type': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '20'}), |
572 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
573 | + }, |
574 | + u'maasserver.physicalblockdevice': { |
575 | + 'Meta': {'ordering': "[u'id']", 'object_name': 'PhysicalBlockDevice', '_ormbases': [u'maasserver.BlockDevice']}, |
576 | + u'blockdevice_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['maasserver.BlockDevice']", 'unique': 'True', 'primary_key': 'True'}), |
577 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
578 | + 'serial': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}) |
579 | + }, |
580 | + u'maasserver.space': { |
581 | + 'Meta': {'object_name': 'Space'}, |
582 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
583 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
584 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), |
585 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
586 | + }, |
587 | + u'maasserver.sshkey': { |
588 | + 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'}, |
589 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
590 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
591 | + 'key': ('django.db.models.fields.TextField', [], {}), |
592 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
593 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) |
594 | + }, |
595 | + u'maasserver.sslkey': { |
596 | + 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSLKey'}, |
597 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
598 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
599 | + 'key': ('django.db.models.fields.TextField', [], {}), |
600 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
601 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) |
602 | + }, |
603 | + u'maasserver.staticipaddress': { |
604 | + 'Meta': {'object_name': 'StaticIPAddress'}, |
605 | + 'alloc_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
606 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
607 | + 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), |
608 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
609 | + 'ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'unique': 'True', 'null': 'True', 'blank': 'True'}), |
610 | + 'subnet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Subnet']", 'null': 'True', 'blank': 'True'}), |
611 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
612 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}) |
613 | + }, |
614 | + u'maasserver.subnet': { |
615 | + 'Meta': {'unique_together': "((u'name', u'space'),)", 'object_name': 'Subnet'}, |
616 | + 'cidr': ('maasserver.fields.CIDRField', [], {'unique': 'True'}), |
617 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
618 | + 'dns_servers': ('djorm_pgarray.fields.ArrayField', [], {'default': '[]', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}), |
619 | + 'gateway_ip': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}), |
620 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
621 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
622 | + 'space': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Space']", 'on_delete': 'models.PROTECT'}), |
623 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
624 | + 'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.VLAN']", 'on_delete': 'models.PROTECT'}) |
625 | + }, |
626 | + u'maasserver.tag': { |
627 | + 'Meta': {'object_name': 'Tag'}, |
628 | + 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
629 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
630 | + 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
631 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
632 | + 'kernel_opts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), |
633 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), |
634 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
635 | + }, |
636 | + u'maasserver.userprofile': { |
637 | + 'Meta': {'object_name': 'UserProfile'}, |
638 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
639 | + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) |
640 | + }, |
641 | + u'maasserver.virtualblockdevice': { |
642 | + 'Meta': {'ordering': "[u'id']", 'object_name': 'VirtualBlockDevice', '_ormbases': [u'maasserver.BlockDevice']}, |
643 | + u'blockdevice_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['maasserver.BlockDevice']", 'unique': 'True', 'primary_key': 'True'}), |
644 | + 'filesystem_group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'virtual_devices'", 'to': u"orm['maasserver.FilesystemGroup']"}), |
645 | + 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}) |
646 | + }, |
647 | + u'maasserver.vlan': { |
648 | + 'Meta': {'unique_together': "((u'vid', u'fabric'),)", 'object_name': 'VLAN'}, |
649 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
650 | + 'fabric': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Fabric']"}), |
651 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
652 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}), |
653 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
654 | + 'vid': ('django.db.models.fields.IntegerField', [], {}) |
655 | + }, |
656 | + u'maasserver.zone': { |
657 | + 'Meta': {'ordering': "[u'name']", 'object_name': 'Zone'}, |
658 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
659 | + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
660 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
661 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), |
662 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
663 | + }, |
664 | + u'piston.consumer': { |
665 | + 'Meta': {'object_name': 'Consumer'}, |
666 | + 'description': ('django.db.models.fields.TextField', [], {}), |
667 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
668 | + 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), |
669 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
670 | + 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
671 | + 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}), |
672 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': u"orm['auth.User']"}) |
673 | + }, |
674 | + u'piston.token': { |
675 | + 'Meta': {'object_name': 'Token'}, |
676 | + 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), |
677 | + 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
678 | + 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Consumer']"}), |
679 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
680 | + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
681 | + 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), |
682 | + 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
683 | + 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1444367666L'}), |
684 | + 'token_type': ('django.db.models.fields.IntegerField', [], {}), |
685 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': u"orm['auth.User']"}), |
686 | + 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'}) |
687 | + } |
688 | + } |
689 | + |
690 | + complete_apps = ['maasserver'] |
691 | \ No newline at end of file |
692 | |
693 | === modified file 'src/maasserver/models/fabric.py' |
694 | --- src/maasserver/models/fabric.py 2015-08-13 23:00:22 +0000 |
695 | +++ src/maasserver/models/fabric.py 2015-10-09 21:26:30 +0000 |
696 | @@ -101,7 +101,7 @@ |
697 | objects = FabricManager() |
698 | |
699 | name = CharField( |
700 | - max_length=256, unique=True, editable=True, |
701 | + max_length=256, editable=True, null=True, blank=True, |
702 | validators=[FABRIC_NAME_VALIDATOR]) |
703 | |
704 | def __unicode__(self): |
705 | @@ -114,6 +114,13 @@ |
706 | def get_default_vlan(self): |
707 | return self.vlan_set.all().order_by('id').first() |
708 | |
709 | + def get_name(self): |
710 | + """Return the name of the fabric.""" |
711 | + if self.name: |
712 | + return self.name |
713 | + else: |
714 | + return "fabric-%s" % self.id |
715 | + |
716 | def delete(self): |
717 | if self.is_default(): |
718 | raise ValidationError( |
719 | |
720 | === modified file 'src/maasserver/models/tests/test_fabric.py' |
721 | --- src/maasserver/models/tests/test_fabric.py 2015-08-28 02:34:50 +0000 |
722 | +++ src/maasserver/models/tests/test_fabric.py 2015-10-09 21:26:30 +0000 |
723 | @@ -90,6 +90,15 @@ |
724 | |
725 | class TestFabric(MAASServerTestCase): |
726 | |
727 | + def test_get_name_for_empty_name(self): |
728 | + fabric = factory.make_Fabric() |
729 | + self.assertEquals("fabric-%s" % fabric.id, fabric.get_name()) |
730 | + |
731 | + def test_get_name_for_set_name(self): |
732 | + name = factory.make_name('name') |
733 | + fabric = factory.make_Fabric(name=name) |
734 | + self.assertEquals(name, fabric.get_name()) |
735 | + |
736 | def test_creates_fabric_with_default_vlan(self): |
737 | name = factory.make_name('name') |
738 | fabric = factory.make_Fabric(name=name) |
739 | |
740 | === modified file 'src/maasserver/models/tests/test_vlan.py' |
741 | --- src/maasserver/models/tests/test_vlan.py 2015-08-28 02:34:50 +0000 |
742 | +++ src/maasserver/models/tests/test_vlan.py 2015-10-09 21:26:30 +0000 |
743 | @@ -34,6 +34,15 @@ |
744 | |
745 | class VLANTest(MAASServerTestCase): |
746 | |
747 | + def test_get_name_for_default_vlan_is_untagged(self): |
748 | + fabric = factory.make_Fabric() |
749 | + self.assertEquals("untagged", fabric.get_default_vlan().get_name()) |
750 | + |
751 | + def test_get_name_for_set_name(self): |
752 | + name = factory.make_name('name') |
753 | + vlan = factory.make_VLAN(name=name) |
754 | + self.assertEquals(name, vlan.get_name()) |
755 | + |
756 | def test_creates_vlan(self): |
757 | name = factory.make_name('name') |
758 | vid = random.randint(3, 55) |
759 | |
760 | === modified file 'src/maasserver/models/vlan.py' |
761 | --- src/maasserver/models/vlan.py 2015-06-19 09:53:30 +0000 |
762 | +++ src/maasserver/models/vlan.py 2015-10-09 21:26:30 +0000 |
763 | @@ -66,19 +66,18 @@ |
764 | verbose_name_plural = "VLANs" |
765 | unique_together = ( |
766 | ('vid', 'fabric'), |
767 | - ('name', 'fabric'), |
768 | ) |
769 | |
770 | name = CharField( |
771 | - max_length=256, editable=True, validators=[VLAN_NAME_VALIDATOR]) |
772 | + max_length=256, editable=True, null=True, blank=True, |
773 | + validators=[VLAN_NAME_VALIDATOR]) |
774 | |
775 | vid = IntegerField(editable=True) |
776 | |
777 | fabric = ForeignKey('Fabric', blank=False, editable=True) |
778 | |
779 | def __unicode__(self): |
780 | - return "name=%s, vid=%d, fabric=%s" % ( |
781 | - self.name, self.vid, self.fabric.name) |
782 | + return "%s.%s" % (self.fabric.get_name(), self.get_name()) |
783 | |
784 | def clean_vid(self): |
785 | if self.vid < 0 or self.vid > 4095: |
786 | @@ -93,6 +92,13 @@ |
787 | """Is this the default VLAN in the fabric?""" |
788 | return self.fabric.get_default_vlan() == self |
789 | |
790 | + def get_name(self): |
791 | + """Return the name of the VLAN.""" |
792 | + if self.is_fabric_default(): |
793 | + return "untagged" |
794 | + else: |
795 | + return self.name |
796 | + |
797 | def manage_connected_interfaces(self): |
798 | """Deal with connected interfaces: |
799 | |
800 | |
801 | === modified file 'src/maasserver/static/js/angular/controllers/node_details_networking.js' |
802 | --- src/maasserver/static/js/angular/controllers/node_details_networking.js 2015-10-06 05:50:44 +0000 |
803 | +++ src/maasserver/static/js/angular/controllers/node_details_networking.js 2015-10-09 21:26:30 +0000 |
804 | @@ -61,6 +61,26 @@ |
805 | }); |
806 | |
807 | |
808 | +// Filter that is specific to the NodeNetworkingController. Remove the default |
809 | +// VLAN if the interface is a VLAN interface. |
810 | +angular.module('MAAS').filter('removeDefaultVLANIfVLAN', function() { |
811 | + return function(vlans, interfaceType) { |
812 | + if(!angular.isString(interfaceType)) { |
813 | + return vlans; |
814 | + } |
815 | + var filtered = []; |
816 | + angular.forEach(vlans, function(vlan) { |
817 | + if(interfaceType !== "vlan") { |
818 | + filtered.push(vlan); |
819 | + } else if(vlan.vid !== 0) { |
820 | + filtered.push(vlan); |
821 | + } |
822 | + }); |
823 | + return filtered; |
824 | + }; |
825 | +}); |
826 | + |
827 | + |
828 | angular.module('MAAS').controller('NodeNetworkingController', [ |
829 | '$scope', '$filter', 'FabricsManager', 'VLANsManager', 'SubnetsManager', |
830 | 'NodesManager', 'GeneralManager', 'ManagerHelperService', |
831 | @@ -332,7 +352,8 @@ |
832 | // Return list of unused VLANs for an interface. Also remove the |
833 | // ignoreVLANs from the returned list. |
834 | function getUnusedVLANs(nic, ignoreVLANs) { |
835 | - var vlans = $filter('filterByFabric')($scope.vlans, nic.fabric); |
836 | + var vlans = $filter('removeDefaultVLAN')($scope.vlans); |
837 | + vlans = $filter('filterByFabric')(vlans, nic.fabric); |
838 | vlans = $filter('filterByUnusedForInterface')( |
839 | vlans, nic, $scope.originalInterfaces); |
840 | |
841 | @@ -413,7 +434,9 @@ |
842 | return ""; |
843 | } |
844 | |
845 | - if(angular.isString(vlan.name) && vlan.name.length > 0) { |
846 | + if(vlan.vid === 0) { |
847 | + return "untagged"; |
848 | + } else if(angular.isString(vlan.name) && vlan.name.length > 0) { |
849 | return vlan.vid + " (" + vlan.name + ")"; |
850 | } else { |
851 | return vlan.vid; |
852 | @@ -624,7 +647,8 @@ |
853 | // Return True if the interface IP address that the user typed is |
854 | // invalid. |
855 | $scope.isIPAddressInvalid = function(nic) { |
856 | - return (nic.ip_address.length === 0 || |
857 | + return (!angular.isString(nic.ip_address) || |
858 | + nic.ip_address.length === 0 || |
859 | !ValidationService.validateIP(nic.ip_address) || |
860 | !ValidationService.validateIPInNetwork( |
861 | nic.ip_address, nic.subnet.cidr)); |
862 | @@ -907,6 +931,7 @@ |
863 | } else { |
864 | $scope.selectedMode = SELECTION_MODE.NONE; |
865 | $scope.selectedInterfaces = []; |
866 | + $scope.newInterface = {}; |
867 | } |
868 | }; |
869 | |
870 | |
871 | === modified file 'src/maasserver/static/js/angular/controllers/subnets_list.js' |
872 | --- src/maasserver/static/js/angular/controllers/subnets_list.js 2015-10-06 22:24:06 +0000 |
873 | +++ src/maasserver/static/js/angular/controllers/subnets_list.js 2015-10-09 21:26:30 +0000 |
874 | @@ -104,7 +104,9 @@ |
875 | // Return the name name for the VLAN. |
876 | function getVLANName(vlan) { |
877 | var name = vlan.vid; |
878 | - if(angular.isString(vlan.name) && vlan.name !== "") { |
879 | + if(vlan.vid === 0) { |
880 | + name = "untagged"; |
881 | + } else if(angular.isString(vlan.name) && vlan.name !== "") { |
882 | name += " (" + vlan.name + ")"; |
883 | } |
884 | return name; |
885 | |
886 | === modified file 'src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js' |
887 | --- src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js 2015-10-06 05:50:44 +0000 |
888 | +++ src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js 2015-10-09 21:26:30 +0000 |
889 | @@ -26,7 +26,7 @@ |
890 | expect(filterByUnusedForInterface(vlans)).toEqual([]); |
891 | }); |
892 | |
893 | - it("returns empty if undefined originalInterfaces", function() { |
894 | + it("returns only free vlans", function() { |
895 | var i, vlan, used_vlans = [], free_vlans = [], all_vlans = []; |
896 | for(i = 0; i < 3; i++) { |
897 | vlan = { |
898 | @@ -125,6 +125,48 @@ |
899 | }); |
900 | |
901 | |
902 | +describe("removeDefaultVLANIfVLAN", function() { |
903 | + |
904 | + // Load the MAAS module. |
905 | + beforeEach(module("MAAS")); |
906 | + |
907 | + // Load the removeDefaultVLANIfVLAN. |
908 | + var removeDefaultVLANIfVLAN; |
909 | + beforeEach(inject(function($filter) { |
910 | + removeDefaultVLANIfVLAN = $filter("removeDefaultVLANIfVLAN"); |
911 | + })); |
912 | + |
913 | + it("returns vlans if undefined type", function() { |
914 | + var i, vlan, vlans = []; |
915 | + for(i = 0; i < 3; i++) { |
916 | + vlan = { |
917 | + id: i, |
918 | + vid: i, |
919 | + fabric: 0 |
920 | + }; |
921 | + vlans.push(vlan); |
922 | + } |
923 | + expect(removeDefaultVLANIfVLAN(vlans)).toEqual(vlans); |
924 | + }); |
925 | + |
926 | + it("removes default vlans from vlans", function() { |
927 | + var i, vlan, vlans = []; |
928 | + for(i = 0; i < 3; i++) { |
929 | + vlan = { |
930 | + id: i, |
931 | + vid: i, |
932 | + fabric: 0 |
933 | + }; |
934 | + vlans.push(vlan); |
935 | + } |
936 | + |
937 | + expect( |
938 | + removeDefaultVLANIfVLAN( |
939 | + vlans, "vlan")).toEqual([vlans[1], vlans[2]]); |
940 | + }); |
941 | +}); |
942 | + |
943 | + |
944 | describe("NodeNetworkingController", function() { |
945 | // Load the MAAS module. |
946 | beforeEach(module("MAAS")); |
947 | @@ -2498,10 +2540,11 @@ |
948 | var parent = { |
949 | id: makeInteger(0, 100) |
950 | }; |
951 | + var subnet = {}; |
952 | $scope.newInterface = { |
953 | type: "alias", |
954 | mode: "auto", |
955 | - subnet: {}, |
956 | + subnet: subnet, |
957 | parent: parent |
958 | }; |
959 | $scope.selectedInterfaces = [{}]; |
960 | @@ -2511,11 +2554,12 @@ |
961 | expect($scope.saveInterfaceLink).toHaveBeenCalledWith({ |
962 | id: parent.id, |
963 | mode: "auto", |
964 | - subnet: $scope.newInterface.subnet, |
965 | + subnet: subnet, |
966 | ip_address: "" |
967 | }); |
968 | expect($scope.selectedMode).toBeNull(); |
969 | expect($scope.selectedInterfaces).toEqual([]); |
970 | + expect($scope.newInterface).toEqual({}); |
971 | }); |
972 | |
973 | it("calls createVLANInterface with correct params", function() { |
974 | @@ -2523,16 +2567,18 @@ |
975 | var parent = { |
976 | id: makeInteger(0, 100) |
977 | }; |
978 | + var vlan = { |
979 | + id: makeInteger(0, 100) |
980 | + }; |
981 | + var subnet = { |
982 | + id: makeInteger(0, 100) |
983 | + }; |
984 | $scope.newInterface = { |
985 | type: "vlan", |
986 | mode: "auto", |
987 | parent: parent, |
988 | - vlan: { |
989 | - id: makeInteger(0, 100) |
990 | - }, |
991 | - subnet: { |
992 | - id: makeInteger(0, 100) |
993 | - } |
994 | + vlan: vlan, |
995 | + subnet: subnet |
996 | }; |
997 | $scope.selectedInterfaces = [{}]; |
998 | $scope.selectedMode = "add"; |
999 | @@ -2543,12 +2589,13 @@ |
1000 | expect(NodesManager.createVLANInterface).toHaveBeenCalledWith( |
1001 | node, { |
1002 | parent: parent.id, |
1003 | - vlan: $scope.newInterface.vlan.id, |
1004 | + vlan: vlan.id, |
1005 | mode: "auto", |
1006 | - subnet: $scope.newInterface.subnet.id |
1007 | + subnet: subnet.id |
1008 | }); |
1009 | expect($scope.selectedMode).toBeNull(); |
1010 | expect($scope.selectedInterfaces).toEqual([]); |
1011 | + expect($scope.newInterface).toEqual({}); |
1012 | }); |
1013 | |
1014 | it("calls add again with type", function() { |
1015 | |
1016 | === added file 'src/maasserver/static/js/angular/filters/remove_default_vlan.js' |
1017 | --- src/maasserver/static/js/angular/filters/remove_default_vlan.js 1970-01-01 00:00:00 +0000 |
1018 | +++ src/maasserver/static/js/angular/filters/remove_default_vlan.js 2015-10-09 21:26:30 +0000 |
1019 | @@ -0,0 +1,17 @@ |
1020 | +/* Copyright 2015 Canonical Ltd. This software is licensed under the |
1021 | + * GNU Affero General Public License version 3 (see the file LICENSE). |
1022 | + * |
1023 | + * MAAS Filter to remove the default VLAN as an option. |
1024 | + */ |
1025 | + |
1026 | +angular.module('MAAS').filter('removeDefaultVLAN', function() { |
1027 | + return function(vlans) { |
1028 | + var filtered = []; |
1029 | + angular.forEach(vlans, function(vlan) { |
1030 | + if(vlan.vid !== 0) { |
1031 | + filtered.push(vlan); |
1032 | + } |
1033 | + }); |
1034 | + return filtered; |
1035 | + }; |
1036 | +}); |
1037 | |
1038 | === added file 'src/maasserver/static/js/angular/filters/tests/test_remove_default_vlan.js' |
1039 | --- src/maasserver/static/js/angular/filters/tests/test_remove_default_vlan.js 1970-01-01 00:00:00 +0000 |
1040 | +++ src/maasserver/static/js/angular/filters/tests/test_remove_default_vlan.js 2015-10-09 21:26:30 +0000 |
1041 | @@ -0,0 +1,30 @@ |
1042 | +/* Copyright 2015 Canonical Ltd. This software is licensed under the |
1043 | + * GNU Affero General Public License version 3 (see the file LICENSE). |
1044 | + * |
1045 | + * Unit tests for removeDefaultVLAN. |
1046 | + */ |
1047 | + |
1048 | +describe("removeDefaultVLAN", function() { |
1049 | + |
1050 | + // Load the MAAS module. |
1051 | + beforeEach(module("MAAS")); |
1052 | + |
1053 | + // Load the removeDefaultVLAN. |
1054 | + var removeDefaultVLAN; |
1055 | + beforeEach(inject(function($filter) { |
1056 | + removeDefaultVLAN = $filter("removeDefaultVLAN"); |
1057 | + })); |
1058 | + |
1059 | + it("only returns vlans without vid 0", function() { |
1060 | + var i, vlan, vlans = []; |
1061 | + for(i = 0; i < 3; i++) { |
1062 | + vlan = { |
1063 | + id: i, |
1064 | + vid: i, |
1065 | + fabric: 0 |
1066 | + }; |
1067 | + vlans.push(vlan); |
1068 | + } |
1069 | + expect(removeDefaultVLAN(vlans)).toEqual([vlans[1], vlans[2]]); |
1070 | + }); |
1071 | +}); |
1072 | |
1073 | === modified file 'src/maasserver/static/partials/node-details.html' |
1074 | --- src/maasserver/static/partials/node-details.html 2015-10-09 07:58:28 +0000 |
1075 | +++ src/maasserver/static/partials/node-details.html 2015-10-09 21:26:30 +0000 |
1076 | @@ -340,9 +340,9 @@ |
1077 | <label class="checkbox-label" for="{$ getUniqueKey(interface) $}"></label> |
1078 | </div> |
1079 | <div class="table__data table__column--15" data-ng-show="column == 'name'"> |
1080 | - <span class="ng-hide" data-ng-show="interface.type == 'alias'">{$ interface.name $}</span> |
1081 | + <span class="ng-hide" data-ng-show="interface.type == 'alias' || interface.type == 'vlan'">{$ interface.name $}</span> |
1082 | <input type="text" class="table__input" |
1083 | - data-ng-show="interface.type != 'alias'" |
1084 | + data-ng-show="interface.type != 'alias' && interface.type != 'vlan'" |
1085 | data-ng-model="interface.name" |
1086 | data-maas-enter-blur |
1087 | data-ng-focus="setFocusInterface(interface)" |
1088 | @@ -368,7 +368,7 @@ |
1089 | <div class="table__data table__column--14"> |
1090 | <select class="table__input" name="fabric" id="fabric" |
1091 | data-ng-model="interface.fabric" |
1092 | - data-ng-disabled="interface.type == 'alias'" |
1093 | + data-ng-disabled="interface.type == 'alias' || interface.type == 'vlan'" |
1094 | data-ng-change="fabricChanged(interface)" |
1095 | data-ng-options="fabric as fabric.name for fabric in fabrics"> |
1096 | </select> |
1097 | @@ -376,9 +376,9 @@ |
1098 | <div class="table__data table__column--14"> |
1099 | <select class="table__input" name="vlan" id="vlan" |
1100 | data-ng-model="interface.vlan" |
1101 | - data-ng-disabled="interface.type == 'alias'" |
1102 | + data-ng-disabled="interface.type == 'alias' || interface.vlan.vid === 0" |
1103 | data-ng-change="saveInterface(interface)" |
1104 | - data-ng-options="vlan as getVLANText(vlan) for vlan in vlans | filterByFabric:interface.fabric"> |
1105 | + data-ng-options="vlan as getVLANText(vlan) for vlan in vlans | removeDefaultVLANIfVLAN:interface.type | filterByFabric:interface.fabric"> |
1106 | </select> |
1107 | </div> |
1108 | <div class="table__data table__column--18"> |
1109 | @@ -453,7 +453,7 @@ |
1110 | <select class="table__input" name="vlan" id="vlan" |
1111 | data-ng-model="newInterface.vlan" |
1112 | data-ng-disabled="newInterface.type == 'alias'" |
1113 | - data-ng-options="vlan as getVLANText(vlan) for vlan in vlans | filterByFabric:newInterface.parent.fabric | filterByUnusedForInterface:newInterface.parent:originalInterfaces" |
1114 | + data-ng-options="vlan as getVLANText(vlan) for vlan in vlans | removeDefaultVLAN | filterByFabric:newInterface.parent.fabric | filterByUnusedForInterface:newInterface.parent:originalInterfaces" |
1115 | data-ng-show="newInterface.type === 'vlan'" |
1116 | data-ng-change="addVLANChanged()"> |
1117 | </select> |
1118 | |
1119 | === modified file 'src/maasserver/testing/factory.py' |
1120 | --- src/maasserver/testing/factory.py 2015-09-24 16:22:12 +0000 |
1121 | +++ src/maasserver/testing/factory.py 2015-10-09 21:26:30 +0000 |
1122 | @@ -709,8 +709,6 @@ |
1123 | return fannetwork |
1124 | |
1125 | def make_Fabric(self, name=None): |
1126 | - if name is None: |
1127 | - name = self.make_name('fabric') |
1128 | fabric = Fabric(name=name) |
1129 | fabric.save() |
1130 | return fabric |
1131 | @@ -727,8 +725,6 @@ |
1132 | |
1133 | def make_VLAN(self, name=None, vid=None, fabric=None): |
1134 | assert vid != 0, "VID=0 VLANs are auto-created" |
1135 | - if name is None: |
1136 | - name = self.make_name('vlan') |
1137 | if fabric is None: |
1138 | fabric = Fabric.objects.get_default_fabric() |
1139 | if vid is None: |
1140 | |
1141 | === modified file 'src/maasserver/tests/test_forms_fabric.py' |
1142 | --- src/maasserver/tests/test_forms_fabric.py 2015-08-13 19:29:08 +0000 |
1143 | +++ src/maasserver/tests/test_forms_fabric.py 2015-10-09 21:26:30 +0000 |
1144 | @@ -22,13 +22,6 @@ |
1145 | |
1146 | class TestFabricForm(MAASServerTestCase): |
1147 | |
1148 | - def test__requires_name(self): |
1149 | - form = FabricForm({}) |
1150 | - self.assertFalse(form.is_valid(), form.errors) |
1151 | - self.assertEquals({ |
1152 | - "name": ["This field is required."] |
1153 | - }, form.errors) |
1154 | - |
1155 | def test__creates_fabric(self): |
1156 | fabric_name = factory.make_name("fabric") |
1157 | form = FabricForm({ |
1158 | |
1159 | === modified file 'src/maasserver/tests/test_forms_space.py' |
1160 | --- src/maasserver/tests/test_forms_space.py 2015-09-01 18:29:48 +0000 |
1161 | +++ src/maasserver/tests/test_forms_space.py 2015-10-09 21:26:30 +0000 |
1162 | @@ -39,13 +39,13 @@ |
1163 | self.assertEquals(space_name, space.name) |
1164 | |
1165 | def test__doest_require_name_on_update(self): |
1166 | - space = factory.make_Fabric() |
1167 | + space = factory.make_Space() |
1168 | form = SpaceForm(instance=space, data={}) |
1169 | self.assertTrue(form.is_valid(), form.errors) |
1170 | |
1171 | def test__updates_space(self): |
1172 | new_name = factory.make_name("space") |
1173 | - space = factory.make_Fabric() |
1174 | + space = factory.make_Space() |
1175 | form = SpaceForm(instance=space, data={ |
1176 | "name": new_name, |
1177 | }) |
1178 | |
1179 | === modified file 'src/maasserver/tests/test_forms_vlan.py' |
1180 | --- src/maasserver/tests/test_forms_vlan.py 2015-08-18 15:55:43 +0000 |
1181 | +++ src/maasserver/tests/test_forms_vlan.py 2015-10-09 21:26:30 +0000 |
1182 | @@ -24,12 +24,11 @@ |
1183 | |
1184 | class TestVLANForm(MAASServerTestCase): |
1185 | |
1186 | - def test__requires_name(self): |
1187 | + def test__requires_vid(self): |
1188 | fabric = factory.make_Fabric() |
1189 | form = VLANForm(fabric=fabric, data={}) |
1190 | self.assertFalse(form.is_valid(), form.errors) |
1191 | self.assertEquals({ |
1192 | - "name": ["This field is required."], |
1193 | "vid": [ |
1194 | "This field is required.", |
1195 | "Vid must be between 0 and 4095.", |
1196 | @@ -55,6 +54,16 @@ |
1197 | form = VLANForm(instance=vlan, data={}) |
1198 | self.assertTrue(form.is_valid(), form.errors) |
1199 | |
1200 | + def test__cannot_edit_default_vlan(self): |
1201 | + fabric = factory.make_Fabric() |
1202 | + form = VLANForm(instance=fabric.get_default_vlan(), data={}) |
1203 | + self.assertFalse(form.is_valid(), form.errors) |
1204 | + self.assertEquals({ |
1205 | + "__all__": [ |
1206 | + "Cannot modify the default VLAN for a fabric.", |
1207 | + ], |
1208 | + }, form.errors) |
1209 | + |
1210 | def test__updates_vlan(self): |
1211 | vlan = factory.make_VLAN() |
1212 | new_name = factory.make_name("vlan") |
1213 | |
1214 | === modified file 'src/maasserver/websockets/handlers/fabric.py' |
1215 | --- src/maasserver/websockets/handlers/fabric.py 2015-10-04 22:28:33 +0000 |
1216 | +++ src/maasserver/websockets/handlers/fabric.py 2015-10-09 21:26:30 +0000 |
1217 | @@ -35,6 +35,7 @@ |
1218 | ] |
1219 | |
1220 | def dehydrate(self, obj, data, for_list=False): |
1221 | + data["name"] = obj.get_name() |
1222 | data["vlan_ids"] = [ |
1223 | vlan.id |
1224 | for vlan in obj.vlan_set.all() |
1225 | |
1226 | === modified file 'src/maasserver/websockets/handlers/tests/test_fabric.py' |
1227 | --- src/maasserver/websockets/handlers/tests/test_fabric.py 2015-09-16 20:59:38 +0000 |
1228 | +++ src/maasserver/websockets/handlers/tests/test_fabric.py 2015-10-09 21:26:30 +0000 |
1229 | @@ -26,7 +26,7 @@ |
1230 | def dehydrate_fabric(self, fabric): |
1231 | data = { |
1232 | "id": fabric.id, |
1233 | - "name": fabric.name, |
1234 | + "name": fabric.get_name(), |
1235 | "updated": dehydrate_datetime(fabric.updated), |
1236 | "created": dehydrate_datetime(fabric.created), |
1237 | "vlan_ids": [ |
1238 | |
1239 | === modified file 'src/maasserver/websockets/handlers/tests/test_vlan.py' |
1240 | --- src/maasserver/websockets/handlers/tests/test_vlan.py 2015-09-16 20:59:38 +0000 |
1241 | +++ src/maasserver/websockets/handlers/tests/test_vlan.py 2015-10-09 21:26:30 +0000 |
1242 | @@ -26,7 +26,7 @@ |
1243 | def dehydrate_vlan(self, vlan): |
1244 | data = { |
1245 | "id": vlan.id, |
1246 | - "name": vlan.name, |
1247 | + "name": vlan.get_name(), |
1248 | "vid": vlan.vid, |
1249 | "fabric": vlan.fabric_id, |
1250 | "updated": dehydrate_datetime(vlan.updated), |
1251 | |
1252 | === modified file 'src/maasserver/websockets/handlers/vlan.py' |
1253 | --- src/maasserver/websockets/handlers/vlan.py 2015-09-16 20:59:38 +0000 |
1254 | +++ src/maasserver/websockets/handlers/vlan.py 2015-10-09 21:26:30 +0000 |
1255 | @@ -36,6 +36,7 @@ |
1256 | ] |
1257 | |
1258 | def dehydrate(self, obj, data, for_list=False): |
1259 | + data["name"] = obj.get_name() |
1260 | data["subnet_ids"] = [ |
1261 | subnet.id |
1262 | for subnet in obj.subnet_set.all() |
Just one minor comment - not sure it's desired to not allow any editing of the default VLAN.
It's a minor enough debate that I wouldn't block landing this, but please call it out in the commit message too.