Merge lp:~blake-rouse/maas/fix-vlan-to-untagged into lp:~maas-committers/maas/trunk

Proposed by Blake Rouse
Status: Merged
Approved by: Blake Rouse
Approved revision: no longer in the source branch.
Merged at revision: 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
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.

To post a comment you must log in.
Revision history for this message
Mike Pontillo (mpontillo) wrote :

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.

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

Thanks for the review. I added it to the commit message.

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :
Download full text (994.0 KiB)

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://security.ubuntu.com trusty-security InRelease
Ign http://nova.clouds.archive.ubuntu.com trusty InRelease
Get:1 http://nova.clouds.archive.ubuntu.com trusty-updates InRelease [64.4 kB]
Hit http://nova.clouds.archive.ubuntu.com trusty Release.gpg
Hit http://nova.clouds.archive.ubuntu.com trusty Release
Hit http://security.ubuntu.com trusty-security/main Sources
Hit http://security.ubuntu.com trusty-security/universe Sources
Hit http://security.ubuntu.com trusty-security/main amd64 Packages
Hit http://security.ubuntu.com trusty-security/universe amd64 Packages
Hit http://security.ubuntu.com trusty-security/main Translation-en
Hit http://security.ubuntu.com trusty-security/universe Translation-en
Get:2 http://nova.clouds.archive.ubuntu.com trusty-updates/main Sources [238 kB]
Get:3 http://nova.clouds.archive.ubuntu.com trusty-updates/universe Sources [140 kB]
Get:4 http://nova.clouds.archive.ubuntu.com trusty-updates/main amd64 Packages [628 kB]
Get:5 http://nova.clouds.archive.ubuntu.com trusty-updates/universe amd64 Packages [323 kB]
Get:6 http://nova.clouds.archive.ubuntu.com trusty-updates/main Translation-en [305 kB]
Get:7 http://nova.clouds.archive.ubuntu.com trusty-updates/universe Translation-en [170 kB]
Hit http://nova.clouds.archive.ubuntu.com trusty/main Sources
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Sources
Hit http://nova.clouds.archive.ubuntu.com trusty/main amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty/universe amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Translation-en
Ign http://nova.clouds.archive.ubuntu.com trusty/main Translation-en_US
Ign http://nova.clouds.archive.ubuntu.com trusty/universe Translation-en_US
Fetched 1,868 kB in 4s (466 kB/s)
Reading package lists...
sudo DEBIAN_FRONTEND=noninteractive apt-get -y \
     --no-install-recommends install apache2 authbind bind9 bind9utils build-essential bzr-builddeb chromium-browser chromium-chromedriver curl daemontools debhelper dh-apport dh-systemd distro-info dnsutils firefox freeipmi-tools git gjs ipython isc-dhcp-common libjs-angularjs libjs-jquery libjs-jquery-hotkeys libjs-yui3-full libjs-yui3-min libpq-dev make nodejs-legacy npm pep8 phantomjs postgresql pyflakes python-apt python-bson python-bzrlib python-convoy python-coverage python-crochet python-cssselect python-curtin python-dev python-distro-info python-django python-django-piston python-django-south python-djorm-ext-pgarray python-docutils python-extras python-fixtures python-flake8 python-formencode python-hivex python-httplib2 python-jinja2 python-jsonschema python-lxml python-mock python-netaddr python-netifaces python-nose python-oauth python-openssl python-paramiko python-pexpect python-pip python-pocket-lint python-psycopg2 python-pyinotify python-pyparsing python-seamicroclient python-simplejson python-simplestreams python-sphinx python-subunit python-tempita python-testresources python-testscenarios python-testtools python-twisted pytho...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/api/fabrics.py'
--- src/maasserver/api/fabrics.py 2015-08-18 15:10:54 +0000
+++ src/maasserver/api/fabrics.py 2015-10-09 21:26:30 +0000
@@ -75,6 +75,11 @@
75 return ('fabric_handler', (fabric_id,))75 return ('fabric_handler', (fabric_id,))
7676
77 @classmethod77 @classmethod
78 def name(cls, fabric):
79 """Return the name of the fabric."""
80 return fabric.get_name()
81
82 @classmethod
78 def vlans(cls, fabric):83 def vlans(cls, fabric):
79 """Return VLANs within the specified fabric."""84 """Return VLANs within the specified fabric."""
80 return fabric.vlan_set.all()85 return fabric.vlan_set.all()
8186
=== modified file 'src/maasserver/api/tests/test_fabrics.py'
--- src/maasserver/api/tests/test_fabrics.py 2015-08-13 19:29:08 +0000
+++ src/maasserver/api/tests/test_fabrics.py 2015-10-09 21:26:30 +0000
@@ -82,16 +82,6 @@
82 self.assertEqual(82 self.assertEqual(
83 httplib.FORBIDDEN, response.status_code, response.content)83 httplib.FORBIDDEN, response.status_code, response.content)
8484
85 def test_create_requires_name(self):
86 self.become_admin()
87 uri = get_fabrics_uri()
88 response = self.client.post(uri, {})
89 self.assertEqual(
90 httplib.BAD_REQUEST, response.status_code, response.content)
91 self.assertEqual({
92 "name": ["This field is required."],
93 }, json.loads(response.content))
94
9585
96class TestFabricAPI(APITestCase):86class TestFabricAPI(APITestCase):
9787
@@ -112,7 +102,7 @@
112 parsed_fabric = json.loads(response.content)102 parsed_fabric = json.loads(response.content)
113 self.assertThat(parsed_fabric, ContainsDict({103 self.assertThat(parsed_fabric, ContainsDict({
114 "id": Equals(fabric.id),104 "id": Equals(fabric.id),
115 "name": Equals(fabric.name),105 "name": Equals(fabric.get_name()),
116 }))106 }))
117 self.assertItemsEqual([107 self.assertItemsEqual([
118 vlan.id108 vlan.id
119109
=== modified file 'src/maasserver/api/tests/test_vlans.py'
--- src/maasserver/api/tests/test_vlans.py 2015-08-15 00:08:54 +0000
+++ src/maasserver/api/tests/test_vlans.py 2015-10-09 21:26:30 +0000
@@ -93,7 +93,7 @@
93 self.assertEqual(93 self.assertEqual(
94 httplib.FORBIDDEN, response.status_code, response.content)94 httplib.FORBIDDEN, response.status_code, response.content)
9595
96 def test_create_requires_name_and_vid(self):96 def test_create_requires_vid(self):
97 self.become_admin()97 self.become_admin()
98 fabric = factory.make_Fabric()98 fabric = factory.make_Fabric()
99 uri = get_vlans_uri(fabric)99 uri = get_vlans_uri(fabric)
@@ -101,7 +101,6 @@
101 self.assertEqual(101 self.assertEqual(
102 httplib.BAD_REQUEST, response.status_code, response.content)102 httplib.BAD_REQUEST, response.status_code, response.content)
103 self.assertEqual({103 self.assertEqual({
104 "name": ["This field is required."],
105 "vid": [104 "vid": [
106 "This field is required.",105 "This field is required.",
107 "Vid must be between 0 and 4095."],106 "Vid must be between 0 and 4095."],
@@ -128,9 +127,9 @@
128 parsed_vlan = json.loads(response.content)127 parsed_vlan = json.loads(response.content)
129 self.assertThat(parsed_vlan, ContainsDict({128 self.assertThat(parsed_vlan, ContainsDict({
130 "id": Equals(vlan.id),129 "id": Equals(vlan.id),
131 "name": Equals(vlan.name),130 "name": Equals(vlan.get_name()),
132 "vid": Equals(vlan.vid),131 "vid": Equals(vlan.vid),
133 "fabric": Equals(fabric.name),132 "fabric": Equals(fabric.get_name()),
134 "resource_uri": Equals(get_vlan_uri(vlan)),133 "resource_uri": Equals(get_vlan_uri(vlan)),
135 }))134 }))
136135
137136
=== modified file 'src/maasserver/api/vlans.py'
--- src/maasserver/api/vlans.py 2015-08-18 15:25:03 +0000
+++ src/maasserver/api/vlans.py 2015-10-09 21:26:30 +0000
@@ -90,7 +90,12 @@
90 @classmethod90 @classmethod
91 def fabric(cls, vlan):91 def fabric(cls, vlan):
92 """Return fabric name."""92 """Return fabric name."""
93 return vlan.fabric.name93 return vlan.fabric.get_name()
94
95 @classmethod
96 def name(cls, vlan):
97 """Return the VLAN name."""
98 return vlan.get_name()
9499
95 def read(self, request, fabric_id, vlan_id):100 def read(self, request, fabric_id, vlan_id):
96 """Read VLAN on fabric.101 """Read VLAN on fabric.
97102
=== modified file 'src/maasserver/context_processors.py'
--- src/maasserver/context_processors.py 2015-09-30 22:30:55 +0000
+++ src/maasserver/context_processors.py 2015-10-09 21:26:30 +0000
@@ -76,6 +76,7 @@
76 'js/angular/filters/by_fabric.js',76 'js/angular/filters/by_fabric.js',
77 'js/angular/filters/by_vlan.js',77 'js/angular/filters/by_vlan.js',
78 'js/angular/filters/by_space.js',78 'js/angular/filters/by_space.js',
79 'js/angular/filters/remove_default_vlan.js',
79 'js/angular/controllers/error.js',80 'js/angular/controllers/error.js',
80 'js/angular/controllers/nodes_list.js',81 'js/angular/controllers/nodes_list.js',
81 'js/angular/controllers/add_hardware.js',82 'js/angular/controllers/add_hardware.js',
8283
=== modified file 'src/maasserver/fixtures/dev_fixture.yaml'
--- src/maasserver/fixtures/dev_fixture.yaml 2015-09-27 20:01:05 +0000
+++ src/maasserver/fixtures/dev_fixture.yaml 2015-10-09 21:26:30 +0000
@@ -9484,11 +9484,11 @@
9484 updated: ! '2015-09-18 15:38:26.981744'}9484 updated: ! '2015-09-18 15:38:26.981744'}
9485 model: maasserver.event9485 model: maasserver.event
9486 pk: 2369486 pk: 236
9487- fields: {created: ! '2015-09-18 15:14:45.727015', name: Default fabric, updated: ! '2015-09-189487- fields: {created: ! '2015-09-18 15:14:45.727015', updated: ! '2015-09-18
9488 15:14:45.727015'}9488 15:14:45.727015'}
9489 model: maasserver.fabric9489 model: maasserver.fabric
9490 pk: 09490 pk: 0
9491- fields: {created: ! '2015-09-18 15:14:45.727015', name: Extra fabric, updated: ! '2015-09-189491- fields: {created: ! '2015-09-18 15:14:45.727015', updated: ! '2015-09-18
9492 15:14:45.727015'}9492 15:14:45.727015'}
9493 model: maasserver.fabric9493 model: maasserver.fabric
9494 pk: 19494 pk: 1
@@ -9512,11 +9512,11 @@
9512- fields: {filesystem_group: 4, uuid: 3a140acf-6a98-4572-aa2f-3de2ba56b818}9512- fields: {filesystem_group: 4, uuid: 3a140acf-6a98-4572-aa2f-3de2ba56b818}
9513 model: maasserver.virtualblockdevice9513 model: maasserver.virtualblockdevice
9514 pk: 129514 pk: 12
9515- fields: {created: ! '2015-09-18 15:14:45.727015', fabric: 0, name: Default VLAN,9515- fields: {created: ! '2015-09-18 15:14:45.727015', fabric: 0,
9516 updated: ! '2015-09-18 15:14:45.727015', vid: 0}9516 updated: ! '2015-09-18 15:14:45.727015', vid: 0}
9517 model: maasserver.vlan9517 model: maasserver.vlan
9518 pk: 09518 pk: 0
9519- fields: {created: ! '2015-09-18 15:14:45.727015', fabric: 1, name: Extra VLAN,9519- fields: {created: ! '2015-09-18 15:14:45.727015', fabric: 1,
9520 updated: ! '2015-09-18 15:14:45.727015', vid: 0}9520 updated: ! '2015-09-18 15:14:45.727015', vid: 0}
9521 model: maasserver.vlan9521 model: maasserver.vlan
9522 pk: 19522 pk: 1
95239523
=== modified file 'src/maasserver/forms_vlan.py'
--- src/maasserver/forms_vlan.py 2015-08-14 14:00:46 +0000
+++ src/maasserver/forms_vlan.py 2015-10-09 21:26:30 +0000
@@ -16,6 +16,7 @@
16 "VLANForm",16 "VLANForm",
17]17]
1818
19from django.core.exceptions import ValidationError
19from maasserver.forms import MAASModelForm20from maasserver.forms import MAASModelForm
20from maasserver.models.vlan import VLAN21from maasserver.models.vlan import VLAN
2122
@@ -37,6 +38,13 @@
37 if instance is None and self.fabric is None:38 if instance is None and self.fabric is None:
38 raise ValueError("Form requires either a instance or a fabric.")39 raise ValueError("Form requires either a instance or a fabric.")
3940
41 def clean(self):
42 cleaned_data = super(VLANForm, self).clean()
43 if self.instance.id is not None and self.instance.is_fabric_default():
44 raise ValidationError(
45 "Cannot modify the default VLAN for a fabric.")
46 return cleaned_data
47
40 def save(self):48 def save(self):
41 """Persist the interface into the database."""49 """Persist the interface into the database."""
42 interface = super(VLANForm, self).save(commit=False)50 interface = super(VLANForm, self).save(commit=False)
4351
=== added file 'src/maasserver/migrations/0183_remove_required_name_from_vlan_and_fabric.py'
--- src/maasserver/migrations/0183_remove_required_name_from_vlan_and_fabric.py 1970-01-01 00:00:00 +0000
+++ src/maasserver/migrations/0183_remove_required_name_from_vlan_and_fabric.py 2015-10-09 21:26:30 +0000
@@ -0,0 +1,519 @@
1from django.db import models
2from south.db import db
3# -*- coding: utf-8 -*-
4from south.utils import datetime_utils as datetime
5from south.v2 import SchemaMigration
6
7
8class Migration(SchemaMigration):
9
10 def forwards(self, orm):
11 # Removing unique constraint on 'Fabric', fields ['name']
12 db.delete_unique(u'maasserver_fabric', ['name'])
13
14 # Removing unique constraint on 'VLAN', fields ['name', 'fabric']
15 db.delete_unique(u'maasserver_vlan', ['name', 'fabric_id'])
16
17
18 # Changing field 'VLAN.name'
19 db.alter_column(u'maasserver_vlan', 'name', self.gf('django.db.models.fields.CharField')(max_length=256, null=True))
20
21 # Changing field 'Fabric.name'
22 db.alter_column(u'maasserver_fabric', 'name', self.gf('django.db.models.fields.CharField')(max_length=256, null=True))
23
24 def backwards(self, orm):
25
26 # User chose to not deal with backwards NULL issues for 'VLAN.name'
27 raise RuntimeError("Cannot reverse this migration. 'VLAN.name' and its values cannot be restored.")
28 # Adding unique constraint on 'VLAN', fields ['name', 'fabric']
29 db.create_unique(u'maasserver_vlan', ['name', 'fabric_id'])
30
31
32 # User chose to not deal with backwards NULL issues for 'Fabric.name'
33 raise RuntimeError("Cannot reverse this migration. 'Fabric.name' and its values cannot be restored.")
34 # Adding unique constraint on 'Fabric', fields ['name']
35 db.create_unique(u'maasserver_fabric', ['name'])
36
37
38 models = {
39 u'auth.group': {
40 'Meta': {'object_name': 'Group'},
41 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
42 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
43 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
44 },
45 u'auth.permission': {
46 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
47 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
48 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
49 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
50 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
51 },
52 u'auth.user': {
53 'Meta': {'object_name': 'User'},
54 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
55 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}),
56 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
57 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
58 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
59 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
60 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
61 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
62 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
63 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
64 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
65 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
66 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
67 },
68 u'contenttypes.contenttype': {
69 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
70 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
71 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
72 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
73 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
74 },
75 u'maasserver.blockdevice': {
76 'Meta': {'ordering': "[u'id']", 'unique_together': "((u'node', u'name'),)", 'object_name': 'BlockDevice'},
77 'block_size': ('django.db.models.fields.IntegerField', [], {}),
78 'created': ('django.db.models.fields.DateTimeField', [], {}),
79 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
80 'id_path': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
81 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
82 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}),
83 'size': ('django.db.models.fields.BigIntegerField', [], {}),
84 'tags': ('djorm_pgarray.fields.ArrayField', [], {'default': '[]', 'dbtype': "u'text'", 'blank': 'True'}),
85 'updated': ('django.db.models.fields.DateTimeField', [], {})
86 },
87 u'maasserver.bootresource': {
88 'Meta': {'unique_together': "((u'name', u'architecture'),)", 'object_name': 'BootResource'},
89 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
90 'created': ('django.db.models.fields.DateTimeField', [], {}),
91 'extra': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
92 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
93 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
94 'rtype': ('django.db.models.fields.IntegerField', [], {'max_length': '10'}),
95 'updated': ('django.db.models.fields.DateTimeField', [], {})
96 },
97 u'maasserver.bootresourcefile': {
98 'Meta': {'unique_together': "((u'resource_set', u'filetype'),)", 'object_name': 'BootResourceFile'},
99 'created': ('django.db.models.fields.DateTimeField', [], {}),
100 'extra': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
101 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
102 'filetype': ('django.db.models.fields.CharField', [], {'default': "u'root-tgz'", 'max_length': '20'}),
103 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
104 'largefile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.LargeFile']"}),
105 'resource_set': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'files'", 'to': u"orm['maasserver.BootResourceSet']"}),
106 'updated': ('django.db.models.fields.DateTimeField', [], {})
107 },
108 u'maasserver.bootresourceset': {
109 'Meta': {'unique_together': "((u'resource', u'version'),)", 'object_name': 'BootResourceSet'},
110 'created': ('django.db.models.fields.DateTimeField', [], {}),
111 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
112 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
113 'resource': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'sets'", 'to': u"orm['maasserver.BootResource']"}),
114 'updated': ('django.db.models.fields.DateTimeField', [], {}),
115 'version': ('django.db.models.fields.CharField', [], {'max_length': '255'})
116 },
117 u'maasserver.bootsource': {
118 'Meta': {'object_name': 'BootSource'},
119 'created': ('django.db.models.fields.DateTimeField', [], {}),
120 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
121 'keyring_data': ('maasserver.fields.EditableBinaryField', [], {'blank': 'True'}),
122 'keyring_filename': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
123 'updated': ('django.db.models.fields.DateTimeField', [], {}),
124 'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200'})
125 },
126 u'maasserver.bootsourcecache': {
127 'Meta': {'object_name': 'BootSourceCache'},
128 'arch': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
129 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}),
130 'created': ('django.db.models.fields.DateTimeField', [], {}),
131 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
132 'label': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
133 'os': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
134 'release': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
135 'subarch': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
136 'updated': ('django.db.models.fields.DateTimeField', [], {})
137 },
138 u'maasserver.bootsourceselection': {
139 'Meta': {'unique_together': "((u'boot_source', u'os', u'release'),)", 'object_name': 'BootSourceSelection'},
140 'arches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
141 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}),
142 'created': ('django.db.models.fields.DateTimeField', [], {}),
143 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
144 'labels': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
145 'os': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
146 'release': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
147 'subarches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
148 'updated': ('django.db.models.fields.DateTimeField', [], {})
149 },
150 u'maasserver.cacheset': {
151 'Meta': {'object_name': 'CacheSet'},
152 'created': ('django.db.models.fields.DateTimeField', [], {}),
153 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
154 'updated': ('django.db.models.fields.DateTimeField', [], {})
155 },
156 u'maasserver.candidatename': {
157 'Meta': {'unique_together': "((u'name', u'position'),)", 'object_name': 'CandidateName'},
158 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
159 'name': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
160 'position': ('django.db.models.fields.IntegerField', [], {})
161 },
162 u'maasserver.componenterror': {
163 'Meta': {'object_name': 'ComponentError'},
164 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}),
165 'created': ('django.db.models.fields.DateTimeField', [], {}),
166 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
167 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
168 'updated': ('django.db.models.fields.DateTimeField', [], {})
169 },
170 u'maasserver.config': {
171 'Meta': {'object_name': 'Config'},
172 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
173 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
174 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'})
175 },
176 u'maasserver.downloadprogress': {
177 'Meta': {'object_name': 'DownloadProgress'},
178 'bytes_downloaded': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
179 'created': ('django.db.models.fields.DateTimeField', [], {}),
180 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}),
181 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
182 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
183 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
184 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
185 'updated': ('django.db.models.fields.DateTimeField', [], {})
186 },
187 u'maasserver.event': {
188 'Meta': {'object_name': 'Event'},
189 'created': ('django.db.models.fields.DateTimeField', [], {}),
190 'description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}),
191 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
192 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}),
193 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.EventType']"}),
194 'updated': ('django.db.models.fields.DateTimeField', [], {})
195 },
196 u'maasserver.eventtype': {
197 'Meta': {'object_name': 'EventType'},
198 'created': ('django.db.models.fields.DateTimeField', [], {}),
199 'description': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
200 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
201 'level': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
202 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
203 'updated': ('django.db.models.fields.DateTimeField', [], {})
204 },
205 u'maasserver.fabric': {
206 'Meta': {'object_name': 'Fabric'},
207 'created': ('django.db.models.fields.DateTimeField', [], {}),
208 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
209 'name': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}),
210 'updated': ('django.db.models.fields.DateTimeField', [], {})
211 },
212 u'maasserver.fannetwork': {
213 'Meta': {'object_name': 'FanNetwork'},
214 'bridge': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
215 'created': ('django.db.models.fields.DateTimeField', [], {}),
216 'dhcp': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
217 'host_reserve': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}),
218 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
219 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
220 'off': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
221 'overlay': ('maasserver.fields.IPv4CIDRField', [], {'unique': 'True'}),
222 'underlay': ('maasserver.fields.IPv4CIDRField', [], {'unique': 'True'}),
223 'updated': ('django.db.models.fields.DateTimeField', [], {})
224 },
225 u'maasserver.filestorage': {
226 'Meta': {'unique_together': "((u'filename', u'owner'),)", 'object_name': 'FileStorage'},
227 'content': ('metadataserver.fields.BinaryField', [], {'blank': 'True'}),
228 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
229 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
230 'key': ('django.db.models.fields.CharField', [], {'default': "u'a1d75a4c-6e44-11e5-a1dc-c48e8ffbb365'", 'unique': 'True', 'max_length': '36'}),
231 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
232 },
233 u'maasserver.filesystem': {
234 'Meta': {'unique_together': "((u'partition', u'acquired'), (u'block_device', u'acquired'))", 'object_name': 'Filesystem'},
235 'acquired': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
236 'block_device': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BlockDevice']", 'null': 'True', 'blank': 'True'}),
237 'cache_set': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'filesystems'", 'null': 'True', 'to': u"orm['maasserver.CacheSet']"}),
238 'create_params': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
239 'created': ('django.db.models.fields.DateTimeField', [], {}),
240 'filesystem_group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'filesystems'", 'null': 'True', 'to': u"orm['maasserver.FilesystemGroup']"}),
241 'fstype': ('django.db.models.fields.CharField', [], {'default': "u'ext4'", 'max_length': '20'}),
242 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
243 'label': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
244 'mount_params': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
245 'mount_point': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
246 'partition': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Partition']", 'null': 'True', 'blank': 'True'}),
247 'updated': ('django.db.models.fields.DateTimeField', [], {}),
248 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '36'})
249 },
250 u'maasserver.filesystemgroup': {
251 'Meta': {'object_name': 'FilesystemGroup'},
252 'cache_mode': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}),
253 'cache_set': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.CacheSet']", 'null': 'True', 'blank': 'True'}),
254 'create_params': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
255 'created': ('django.db.models.fields.DateTimeField', [], {}),
256 'group_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
257 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
258 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
259 'updated': ('django.db.models.fields.DateTimeField', [], {}),
260 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
261 },
262 u'maasserver.interface': {
263 'Meta': {'ordering': "(u'created',)", 'object_name': 'Interface'},
264 'created': ('django.db.models.fields.DateTimeField', [], {}),
265 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
266 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
267 'ip_addresses': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['maasserver.StaticIPAddress']", 'null': 'True', 'blank': 'True'}),
268 'ipv4_params': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
269 'ipv6_params': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
270 'mac_address': ('maasserver.fields.MACAddressField', [], {'null': 'True', 'blank': 'True'}),
271 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
272 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']", 'null': 'True', 'blank': 'True'}),
273 'params': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
274 'parents': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['maasserver.Interface']", 'null': 'True', 'through': u"orm['maasserver.InterfaceRelationship']", 'blank': 'True'}),
275 'tags': ('djorm_pgarray.fields.ArrayField', [], {'default': '[]', 'dbtype': "u'text'", 'blank': 'True'}),
276 'type': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
277 'updated': ('django.db.models.fields.DateTimeField', [], {}),
278 'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.VLAN']", 'on_delete': 'models.PROTECT'})
279 },
280 u'maasserver.interfacerelationship': {
281 'Meta': {'object_name': 'InterfaceRelationship'},
282 'child': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'parent_relationships'", 'to': u"orm['maasserver.Interface']"}),
283 'created': ('django.db.models.fields.DateTimeField', [], {}),
284 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
285 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'children_relationships'", 'to': u"orm['maasserver.Interface']"}),
286 'updated': ('django.db.models.fields.DateTimeField', [], {})
287 },
288 u'maasserver.largefile': {
289 'Meta': {'object_name': 'LargeFile'},
290 'content': ('maasserver.fields.LargeObjectField', [], {}),
291 'created': ('django.db.models.fields.DateTimeField', [], {}),
292 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
293 'sha256': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}),
294 'total_size': ('django.db.models.fields.BigIntegerField', [], {}),
295 'updated': ('django.db.models.fields.DateTimeField', [], {})
296 },
297 u'maasserver.licensekey': {
298 'Meta': {'unique_together': "((u'osystem', u'distro_series'),)", 'object_name': 'LicenseKey'},
299 'created': ('django.db.models.fields.DateTimeField', [], {}),
300 'distro_series': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
301 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
302 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
303 'osystem': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
304 'updated': ('django.db.models.fields.DateTimeField', [], {})
305 },
306 u'maasserver.node': {
307 'Meta': {'object_name': 'Node'},
308 'agent_name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
309 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '31', 'null': 'True', 'blank': 'True'}),
310 'bios_boot_method': ('django.db.models.fields.CharField', [], {'max_length': '31', 'null': 'True', 'blank': 'True'}),
311 'block_poweroff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
312 'boot_cluster_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
313 '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'}),
314 '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'}),
315 'boot_type': ('django.db.models.fields.CharField', [], {'default': "u'fastpath'", 'max_length': '20'}),
316 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
317 'created': ('django.db.models.fields.DateTimeField', [], {}),
318 'disable_ipv4': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
319 'distro_series': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
320 'enable_ssh': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
321 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
322 'error_description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}),
323 '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'}),
324 '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'}),
325 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}),
326 'hwe_kernel': ('django.db.models.fields.CharField', [], {'max_length': '31', 'null': 'True', 'blank': 'True'}),
327 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
328 'installable': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
329 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}),
330 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
331 'min_hwe_kernel': ('django.db.models.fields.CharField', [], {'max_length': '31', 'null': 'True', 'blank': 'True'}),
332 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
333 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}),
334 'osystem': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
335 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
336 'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "u'children'", 'null': 'True', 'blank': 'True', 'to': u"orm['maasserver.Node']"}),
337 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'max_length': '32768', 'blank': 'True'}),
338 'power_state': ('django.db.models.fields.CharField', [], {'default': "u'unknown'", 'max_length': '10'}),
339 'power_state_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
340 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}),
341 'routers': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'macaddr'", 'null': 'True', 'blank': 'True'}),
342 'skip_networking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
343 'skip_storage': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
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-a1d55db4-6e44-11e5-a1dc-c48e8ffbb365'", '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 'static_ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
380 'static_ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
381 'subnet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Subnet']", 'null': 'True', 'on_delete': 'models.PROTECT', 'blank': 'True'}),
382 'updated': ('django.db.models.fields.DateTimeField', [], {}),
383 'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.VLAN']", 'on_delete': 'models.PROTECT'})
384 },
385 u'maasserver.partition': {
386 'Meta': {'object_name': 'Partition'},
387 'bootable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
388 'created': ('django.db.models.fields.DateTimeField', [], {}),
389 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
390 'partition_table': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'partitions'", 'to': u"orm['maasserver.PartitionTable']"}),
391 'size': ('django.db.models.fields.BigIntegerField', [], {}),
392 'updated': ('django.db.models.fields.DateTimeField', [], {}),
393 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '36', 'unique': 'True', 'null': 'True', 'blank': 'True'})
394 },
395 u'maasserver.partitiontable': {
396 'Meta': {'object_name': 'PartitionTable'},
397 'block_device': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BlockDevice']"}),
398 'created': ('django.db.models.fields.DateTimeField', [], {}),
399 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
400 'table_type': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '20'}),
401 'updated': ('django.db.models.fields.DateTimeField', [], {})
402 },
403 u'maasserver.physicalblockdevice': {
404 'Meta': {'ordering': "[u'id']", 'object_name': 'PhysicalBlockDevice', '_ormbases': [u'maasserver.BlockDevice']},
405 u'blockdevice_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['maasserver.BlockDevice']", 'unique': 'True', 'primary_key': 'True'}),
406 'model': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
407 'serial': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
408 },
409 u'maasserver.space': {
410 'Meta': {'object_name': 'Space'},
411 'created': ('django.db.models.fields.DateTimeField', [], {}),
412 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
413 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
414 'updated': ('django.db.models.fields.DateTimeField', [], {})
415 },
416 u'maasserver.sshkey': {
417 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'},
418 'created': ('django.db.models.fields.DateTimeField', [], {}),
419 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
420 'key': ('django.db.models.fields.TextField', [], {}),
421 'updated': ('django.db.models.fields.DateTimeField', [], {}),
422 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
423 },
424 u'maasserver.sslkey': {
425 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSLKey'},
426 'created': ('django.db.models.fields.DateTimeField', [], {}),
427 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
428 'key': ('django.db.models.fields.TextField', [], {}),
429 'updated': ('django.db.models.fields.DateTimeField', [], {}),
430 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
431 },
432 u'maasserver.staticipaddress': {
433 'Meta': {'object_name': 'StaticIPAddress'},
434 'alloc_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
435 'created': ('django.db.models.fields.DateTimeField', [], {}),
436 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
437 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
438 'ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
439 'subnet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Subnet']", 'null': 'True', 'blank': 'True'}),
440 'updated': ('django.db.models.fields.DateTimeField', [], {}),
441 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
442 },
443 u'maasserver.subnet': {
444 'Meta': {'unique_together': "((u'name', u'space'),)", 'object_name': 'Subnet'},
445 'cidr': ('maasserver.fields.CIDRField', [], {'unique': 'True'}),
446 'created': ('django.db.models.fields.DateTimeField', [], {}),
447 'dns_servers': ('djorm_pgarray.fields.ArrayField', [], {'default': '[]', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
448 'gateway_ip': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
449 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
450 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
451 'space': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Space']", 'on_delete': 'models.PROTECT'}),
452 'updated': ('django.db.models.fields.DateTimeField', [], {}),
453 'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.VLAN']", 'on_delete': 'models.PROTECT'})
454 },
455 u'maasserver.tag': {
456 'Meta': {'object_name': 'Tag'},
457 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
458 'created': ('django.db.models.fields.DateTimeField', [], {}),
459 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
460 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
461 'kernel_opts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
462 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
463 'updated': ('django.db.models.fields.DateTimeField', [], {})
464 },
465 u'maasserver.userprofile': {
466 'Meta': {'object_name': 'UserProfile'},
467 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
468 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'})
469 },
470 u'maasserver.virtualblockdevice': {
471 'Meta': {'ordering': "[u'id']", 'object_name': 'VirtualBlockDevice', '_ormbases': [u'maasserver.BlockDevice']},
472 u'blockdevice_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['maasserver.BlockDevice']", 'unique': 'True', 'primary_key': 'True'}),
473 'filesystem_group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'virtual_devices'", 'to': u"orm['maasserver.FilesystemGroup']"}),
474 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
475 },
476 u'maasserver.vlan': {
477 'Meta': {'unique_together': "((u'vid', u'fabric'),)", 'object_name': 'VLAN'},
478 'created': ('django.db.models.fields.DateTimeField', [], {}),
479 'fabric': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Fabric']"}),
480 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
481 'name': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}),
482 'updated': ('django.db.models.fields.DateTimeField', [], {}),
483 'vid': ('django.db.models.fields.IntegerField', [], {})
484 },
485 u'maasserver.zone': {
486 'Meta': {'ordering': "[u'name']", 'object_name': 'Zone'},
487 'created': ('django.db.models.fields.DateTimeField', [], {}),
488 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
489 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
490 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
491 'updated': ('django.db.models.fields.DateTimeField', [], {})
492 },
493 u'piston.consumer': {
494 'Meta': {'object_name': 'Consumer'},
495 'description': ('django.db.models.fields.TextField', [], {}),
496 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
497 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
498 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
499 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
500 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}),
501 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': u"orm['auth.User']"})
502 },
503 u'piston.token': {
504 'Meta': {'object_name': 'Token'},
505 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
506 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
507 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Consumer']"}),
508 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
509 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
510 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
511 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
512 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1444367666L'}),
513 'token_type': ('django.db.models.fields.IntegerField', [], {}),
514 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': u"orm['auth.User']"}),
515 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'})
516 }
517 }
518
519 complete_apps = ['maasserver']
0\ No newline at end of file520\ No newline at end of file
1521
=== modified file 'src/maasserver/models/fabric.py'
--- src/maasserver/models/fabric.py 2015-08-13 23:00:22 +0000
+++ src/maasserver/models/fabric.py 2015-10-09 21:26:30 +0000
@@ -101,7 +101,7 @@
101 objects = FabricManager()101 objects = FabricManager()
102102
103 name = CharField(103 name = CharField(
104 max_length=256, unique=True, editable=True,104 max_length=256, editable=True, null=True, blank=True,
105 validators=[FABRIC_NAME_VALIDATOR])105 validators=[FABRIC_NAME_VALIDATOR])
106106
107 def __unicode__(self):107 def __unicode__(self):
@@ -114,6 +114,13 @@
114 def get_default_vlan(self):114 def get_default_vlan(self):
115 return self.vlan_set.all().order_by('id').first()115 return self.vlan_set.all().order_by('id').first()
116116
117 def get_name(self):
118 """Return the name of the fabric."""
119 if self.name:
120 return self.name
121 else:
122 return "fabric-%s" % self.id
123
117 def delete(self):124 def delete(self):
118 if self.is_default():125 if self.is_default():
119 raise ValidationError(126 raise ValidationError(
120127
=== modified file 'src/maasserver/models/tests/test_fabric.py'
--- src/maasserver/models/tests/test_fabric.py 2015-08-28 02:34:50 +0000
+++ src/maasserver/models/tests/test_fabric.py 2015-10-09 21:26:30 +0000
@@ -90,6 +90,15 @@
9090
91class TestFabric(MAASServerTestCase):91class TestFabric(MAASServerTestCase):
9292
93 def test_get_name_for_empty_name(self):
94 fabric = factory.make_Fabric()
95 self.assertEquals("fabric-%s" % fabric.id, fabric.get_name())
96
97 def test_get_name_for_set_name(self):
98 name = factory.make_name('name')
99 fabric = factory.make_Fabric(name=name)
100 self.assertEquals(name, fabric.get_name())
101
93 def test_creates_fabric_with_default_vlan(self):102 def test_creates_fabric_with_default_vlan(self):
94 name = factory.make_name('name')103 name = factory.make_name('name')
95 fabric = factory.make_Fabric(name=name)104 fabric = factory.make_Fabric(name=name)
96105
=== modified file 'src/maasserver/models/tests/test_vlan.py'
--- src/maasserver/models/tests/test_vlan.py 2015-08-28 02:34:50 +0000
+++ src/maasserver/models/tests/test_vlan.py 2015-10-09 21:26:30 +0000
@@ -34,6 +34,15 @@
3434
35class VLANTest(MAASServerTestCase):35class VLANTest(MAASServerTestCase):
3636
37 def test_get_name_for_default_vlan_is_untagged(self):
38 fabric = factory.make_Fabric()
39 self.assertEquals("untagged", fabric.get_default_vlan().get_name())
40
41 def test_get_name_for_set_name(self):
42 name = factory.make_name('name')
43 vlan = factory.make_VLAN(name=name)
44 self.assertEquals(name, vlan.get_name())
45
37 def test_creates_vlan(self):46 def test_creates_vlan(self):
38 name = factory.make_name('name')47 name = factory.make_name('name')
39 vid = random.randint(3, 55)48 vid = random.randint(3, 55)
4049
=== modified file 'src/maasserver/models/vlan.py'
--- src/maasserver/models/vlan.py 2015-06-19 09:53:30 +0000
+++ src/maasserver/models/vlan.py 2015-10-09 21:26:30 +0000
@@ -66,19 +66,18 @@
66 verbose_name_plural = "VLANs"66 verbose_name_plural = "VLANs"
67 unique_together = (67 unique_together = (
68 ('vid', 'fabric'),68 ('vid', 'fabric'),
69 ('name', 'fabric'),
70 )69 )
7170
72 name = CharField(71 name = CharField(
73 max_length=256, editable=True, validators=[VLAN_NAME_VALIDATOR])72 max_length=256, editable=True, null=True, blank=True,
73 validators=[VLAN_NAME_VALIDATOR])
7474
75 vid = IntegerField(editable=True)75 vid = IntegerField(editable=True)
7676
77 fabric = ForeignKey('Fabric', blank=False, editable=True)77 fabric = ForeignKey('Fabric', blank=False, editable=True)
7878
79 def __unicode__(self):79 def __unicode__(self):
80 return "name=%s, vid=%d, fabric=%s" % (80 return "%s.%s" % (self.fabric.get_name(), self.get_name())
81 self.name, self.vid, self.fabric.name)
8281
83 def clean_vid(self):82 def clean_vid(self):
84 if self.vid < 0 or self.vid > 4095:83 if self.vid < 0 or self.vid > 4095:
@@ -93,6 +92,13 @@
93 """Is this the default VLAN in the fabric?"""92 """Is this the default VLAN in the fabric?"""
94 return self.fabric.get_default_vlan() == self93 return self.fabric.get_default_vlan() == self
9594
95 def get_name(self):
96 """Return the name of the VLAN."""
97 if self.is_fabric_default():
98 return "untagged"
99 else:
100 return self.name
101
96 def manage_connected_interfaces(self):102 def manage_connected_interfaces(self):
97 """Deal with connected interfaces:103 """Deal with connected interfaces:
98104
99105
=== modified file 'src/maasserver/static/js/angular/controllers/node_details_networking.js'
--- src/maasserver/static/js/angular/controllers/node_details_networking.js 2015-10-06 05:50:44 +0000
+++ src/maasserver/static/js/angular/controllers/node_details_networking.js 2015-10-09 21:26:30 +0000
@@ -61,6 +61,26 @@
61});61});
6262
6363
64// Filter that is specific to the NodeNetworkingController. Remove the default
65// VLAN if the interface is a VLAN interface.
66angular.module('MAAS').filter('removeDefaultVLANIfVLAN', function() {
67 return function(vlans, interfaceType) {
68 if(!angular.isString(interfaceType)) {
69 return vlans;
70 }
71 var filtered = [];
72 angular.forEach(vlans, function(vlan) {
73 if(interfaceType !== "vlan") {
74 filtered.push(vlan);
75 } else if(vlan.vid !== 0) {
76 filtered.push(vlan);
77 }
78 });
79 return filtered;
80 };
81});
82
83
64angular.module('MAAS').controller('NodeNetworkingController', [84angular.module('MAAS').controller('NodeNetworkingController', [
65 '$scope', '$filter', 'FabricsManager', 'VLANsManager', 'SubnetsManager',85 '$scope', '$filter', 'FabricsManager', 'VLANsManager', 'SubnetsManager',
66 'NodesManager', 'GeneralManager', 'ManagerHelperService',86 'NodesManager', 'GeneralManager', 'ManagerHelperService',
@@ -332,7 +352,8 @@
332 // Return list of unused VLANs for an interface. Also remove the352 // Return list of unused VLANs for an interface. Also remove the
333 // ignoreVLANs from the returned list.353 // ignoreVLANs from the returned list.
334 function getUnusedVLANs(nic, ignoreVLANs) {354 function getUnusedVLANs(nic, ignoreVLANs) {
335 var vlans = $filter('filterByFabric')($scope.vlans, nic.fabric);355 var vlans = $filter('removeDefaultVLAN')($scope.vlans);
356 vlans = $filter('filterByFabric')(vlans, nic.fabric);
336 vlans = $filter('filterByUnusedForInterface')(357 vlans = $filter('filterByUnusedForInterface')(
337 vlans, nic, $scope.originalInterfaces);358 vlans, nic, $scope.originalInterfaces);
338359
@@ -413,7 +434,9 @@
413 return "";434 return "";
414 }435 }
415436
416 if(angular.isString(vlan.name) && vlan.name.length > 0) {437 if(vlan.vid === 0) {
438 return "untagged";
439 } else if(angular.isString(vlan.name) && vlan.name.length > 0) {
417 return vlan.vid + " (" + vlan.name + ")";440 return vlan.vid + " (" + vlan.name + ")";
418 } else {441 } else {
419 return vlan.vid;442 return vlan.vid;
@@ -624,7 +647,8 @@
624 // Return True if the interface IP address that the user typed is647 // Return True if the interface IP address that the user typed is
625 // invalid.648 // invalid.
626 $scope.isIPAddressInvalid = function(nic) {649 $scope.isIPAddressInvalid = function(nic) {
627 return (nic.ip_address.length === 0 ||650 return (!angular.isString(nic.ip_address) ||
651 nic.ip_address.length === 0 ||
628 !ValidationService.validateIP(nic.ip_address) ||652 !ValidationService.validateIP(nic.ip_address) ||
629 !ValidationService.validateIPInNetwork(653 !ValidationService.validateIPInNetwork(
630 nic.ip_address, nic.subnet.cidr));654 nic.ip_address, nic.subnet.cidr));
@@ -907,6 +931,7 @@
907 } else {931 } else {
908 $scope.selectedMode = SELECTION_MODE.NONE;932 $scope.selectedMode = SELECTION_MODE.NONE;
909 $scope.selectedInterfaces = [];933 $scope.selectedInterfaces = [];
934 $scope.newInterface = {};
910 }935 }
911 };936 };
912937
913938
=== modified file 'src/maasserver/static/js/angular/controllers/subnets_list.js'
--- src/maasserver/static/js/angular/controllers/subnets_list.js 2015-10-06 22:24:06 +0000
+++ src/maasserver/static/js/angular/controllers/subnets_list.js 2015-10-09 21:26:30 +0000
@@ -104,7 +104,9 @@
104 // Return the name name for the VLAN.104 // Return the name name for the VLAN.
105 function getVLANName(vlan) {105 function getVLANName(vlan) {
106 var name = vlan.vid;106 var name = vlan.vid;
107 if(angular.isString(vlan.name) && vlan.name !== "") {107 if(vlan.vid === 0) {
108 name = "untagged";
109 } else if(angular.isString(vlan.name) && vlan.name !== "") {
108 name += " (" + vlan.name + ")";110 name += " (" + vlan.name + ")";
109 }111 }
110 return name;112 return name;
111113
=== modified file 'src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js'
--- src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js 2015-10-06 05:50:44 +0000
+++ src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js 2015-10-09 21:26:30 +0000
@@ -26,7 +26,7 @@
26 expect(filterByUnusedForInterface(vlans)).toEqual([]);26 expect(filterByUnusedForInterface(vlans)).toEqual([]);
27 });27 });
2828
29 it("returns empty if undefined originalInterfaces", function() {29 it("returns only free vlans", function() {
30 var i, vlan, used_vlans = [], free_vlans = [], all_vlans = [];30 var i, vlan, used_vlans = [], free_vlans = [], all_vlans = [];
31 for(i = 0; i < 3; i++) {31 for(i = 0; i < 3; i++) {
32 vlan = {32 vlan = {
@@ -125,6 +125,48 @@
125});125});
126126
127127
128describe("removeDefaultVLANIfVLAN", function() {
129
130 // Load the MAAS module.
131 beforeEach(module("MAAS"));
132
133 // Load the removeDefaultVLANIfVLAN.
134 var removeDefaultVLANIfVLAN;
135 beforeEach(inject(function($filter) {
136 removeDefaultVLANIfVLAN = $filter("removeDefaultVLANIfVLAN");
137 }));
138
139 it("returns vlans if undefined type", function() {
140 var i, vlan, vlans = [];
141 for(i = 0; i < 3; i++) {
142 vlan = {
143 id: i,
144 vid: i,
145 fabric: 0
146 };
147 vlans.push(vlan);
148 }
149 expect(removeDefaultVLANIfVLAN(vlans)).toEqual(vlans);
150 });
151
152 it("removes default vlans from vlans", function() {
153 var i, vlan, vlans = [];
154 for(i = 0; i < 3; i++) {
155 vlan = {
156 id: i,
157 vid: i,
158 fabric: 0
159 };
160 vlans.push(vlan);
161 }
162
163 expect(
164 removeDefaultVLANIfVLAN(
165 vlans, "vlan")).toEqual([vlans[1], vlans[2]]);
166 });
167});
168
169
128describe("NodeNetworkingController", function() {170describe("NodeNetworkingController", function() {
129 // Load the MAAS module.171 // Load the MAAS module.
130 beforeEach(module("MAAS"));172 beforeEach(module("MAAS"));
@@ -2498,10 +2540,11 @@
2498 var parent = {2540 var parent = {
2499 id: makeInteger(0, 100)2541 id: makeInteger(0, 100)
2500 };2542 };
2543 var subnet = {};
2501 $scope.newInterface = {2544 $scope.newInterface = {
2502 type: "alias",2545 type: "alias",
2503 mode: "auto",2546 mode: "auto",
2504 subnet: {},2547 subnet: subnet,
2505 parent: parent2548 parent: parent
2506 };2549 };
2507 $scope.selectedInterfaces = [{}];2550 $scope.selectedInterfaces = [{}];
@@ -2511,11 +2554,12 @@
2511 expect($scope.saveInterfaceLink).toHaveBeenCalledWith({2554 expect($scope.saveInterfaceLink).toHaveBeenCalledWith({
2512 id: parent.id,2555 id: parent.id,
2513 mode: "auto",2556 mode: "auto",
2514 subnet: $scope.newInterface.subnet,2557 subnet: subnet,
2515 ip_address: ""2558 ip_address: ""
2516 });2559 });
2517 expect($scope.selectedMode).toBeNull();2560 expect($scope.selectedMode).toBeNull();
2518 expect($scope.selectedInterfaces).toEqual([]);2561 expect($scope.selectedInterfaces).toEqual([]);
2562 expect($scope.newInterface).toEqual({});
2519 });2563 });
25202564
2521 it("calls createVLANInterface with correct params", function() {2565 it("calls createVLANInterface with correct params", function() {
@@ -2523,16 +2567,18 @@
2523 var parent = {2567 var parent = {
2524 id: makeInteger(0, 100)2568 id: makeInteger(0, 100)
2525 };2569 };
2570 var vlan = {
2571 id: makeInteger(0, 100)
2572 };
2573 var subnet = {
2574 id: makeInteger(0, 100)
2575 };
2526 $scope.newInterface = {2576 $scope.newInterface = {
2527 type: "vlan",2577 type: "vlan",
2528 mode: "auto",2578 mode: "auto",
2529 parent: parent,2579 parent: parent,
2530 vlan: {2580 vlan: vlan,
2531 id: makeInteger(0, 100)2581 subnet: subnet
2532 },
2533 subnet: {
2534 id: makeInteger(0, 100)
2535 }
2536 };2582 };
2537 $scope.selectedInterfaces = [{}];2583 $scope.selectedInterfaces = [{}];
2538 $scope.selectedMode = "add";2584 $scope.selectedMode = "add";
@@ -2543,12 +2589,13 @@
2543 expect(NodesManager.createVLANInterface).toHaveBeenCalledWith(2589 expect(NodesManager.createVLANInterface).toHaveBeenCalledWith(
2544 node, {2590 node, {
2545 parent: parent.id,2591 parent: parent.id,
2546 vlan: $scope.newInterface.vlan.id,2592 vlan: vlan.id,
2547 mode: "auto",2593 mode: "auto",
2548 subnet: $scope.newInterface.subnet.id2594 subnet: subnet.id
2549 });2595 });
2550 expect($scope.selectedMode).toBeNull();2596 expect($scope.selectedMode).toBeNull();
2551 expect($scope.selectedInterfaces).toEqual([]);2597 expect($scope.selectedInterfaces).toEqual([]);
2598 expect($scope.newInterface).toEqual({});
2552 });2599 });
25532600
2554 it("calls add again with type", function() {2601 it("calls add again with type", function() {
25552602
=== added file 'src/maasserver/static/js/angular/filters/remove_default_vlan.js'
--- src/maasserver/static/js/angular/filters/remove_default_vlan.js 1970-01-01 00:00:00 +0000
+++ src/maasserver/static/js/angular/filters/remove_default_vlan.js 2015-10-09 21:26:30 +0000
@@ -0,0 +1,17 @@
1/* Copyright 2015 Canonical Ltd. This software is licensed under the
2 * GNU Affero General Public License version 3 (see the file LICENSE).
3 *
4 * MAAS Filter to remove the default VLAN as an option.
5 */
6
7angular.module('MAAS').filter('removeDefaultVLAN', function() {
8 return function(vlans) {
9 var filtered = [];
10 angular.forEach(vlans, function(vlan) {
11 if(vlan.vid !== 0) {
12 filtered.push(vlan);
13 }
14 });
15 return filtered;
16 };
17});
018
=== added file 'src/maasserver/static/js/angular/filters/tests/test_remove_default_vlan.js'
--- src/maasserver/static/js/angular/filters/tests/test_remove_default_vlan.js 1970-01-01 00:00:00 +0000
+++ src/maasserver/static/js/angular/filters/tests/test_remove_default_vlan.js 2015-10-09 21:26:30 +0000
@@ -0,0 +1,30 @@
1/* Copyright 2015 Canonical Ltd. This software is licensed under the
2 * GNU Affero General Public License version 3 (see the file LICENSE).
3 *
4 * Unit tests for removeDefaultVLAN.
5 */
6
7describe("removeDefaultVLAN", function() {
8
9 // Load the MAAS module.
10 beforeEach(module("MAAS"));
11
12 // Load the removeDefaultVLAN.
13 var removeDefaultVLAN;
14 beforeEach(inject(function($filter) {
15 removeDefaultVLAN = $filter("removeDefaultVLAN");
16 }));
17
18 it("only returns vlans without vid 0", function() {
19 var i, vlan, vlans = [];
20 for(i = 0; i < 3; i++) {
21 vlan = {
22 id: i,
23 vid: i,
24 fabric: 0
25 };
26 vlans.push(vlan);
27 }
28 expect(removeDefaultVLAN(vlans)).toEqual([vlans[1], vlans[2]]);
29 });
30});
031
=== modified file 'src/maasserver/static/partials/node-details.html'
--- src/maasserver/static/partials/node-details.html 2015-10-09 07:58:28 +0000
+++ src/maasserver/static/partials/node-details.html 2015-10-09 21:26:30 +0000
@@ -340,9 +340,9 @@
340 <label class="checkbox-label" for="{$ getUniqueKey(interface) $}"></label>340 <label class="checkbox-label" for="{$ getUniqueKey(interface) $}"></label>
341 </div>341 </div>
342 <div class="table__data table__column--15" data-ng-show="column == 'name'">342 <div class="table__data table__column--15" data-ng-show="column == 'name'">
343 <span class="ng-hide" data-ng-show="interface.type == 'alias'">{$ interface.name $}</span>343 <span class="ng-hide" data-ng-show="interface.type == 'alias' || interface.type == 'vlan'">{$ interface.name $}</span>
344 <input type="text" class="table__input"344 <input type="text" class="table__input"
345 data-ng-show="interface.type != 'alias'"345 data-ng-show="interface.type != 'alias' && interface.type != 'vlan'"
346 data-ng-model="interface.name"346 data-ng-model="interface.name"
347 data-maas-enter-blur347 data-maas-enter-blur
348 data-ng-focus="setFocusInterface(interface)"348 data-ng-focus="setFocusInterface(interface)"
@@ -368,7 +368,7 @@
368 <div class="table__data table__column--14">368 <div class="table__data table__column--14">
369 <select class="table__input" name="fabric" id="fabric"369 <select class="table__input" name="fabric" id="fabric"
370 data-ng-model="interface.fabric"370 data-ng-model="interface.fabric"
371 data-ng-disabled="interface.type == 'alias'"371 data-ng-disabled="interface.type == 'alias' || interface.type == 'vlan'"
372 data-ng-change="fabricChanged(interface)"372 data-ng-change="fabricChanged(interface)"
373 data-ng-options="fabric as fabric.name for fabric in fabrics">373 data-ng-options="fabric as fabric.name for fabric in fabrics">
374 </select>374 </select>
@@ -376,9 +376,9 @@
376 <div class="table__data table__column--14">376 <div class="table__data table__column--14">
377 <select class="table__input" name="vlan" id="vlan"377 <select class="table__input" name="vlan" id="vlan"
378 data-ng-model="interface.vlan"378 data-ng-model="interface.vlan"
379 data-ng-disabled="interface.type == 'alias'"379 data-ng-disabled="interface.type == 'alias' || interface.vlan.vid === 0"
380 data-ng-change="saveInterface(interface)"380 data-ng-change="saveInterface(interface)"
381 data-ng-options="vlan as getVLANText(vlan) for vlan in vlans | filterByFabric:interface.fabric">381 data-ng-options="vlan as getVLANText(vlan) for vlan in vlans | removeDefaultVLANIfVLAN:interface.type | filterByFabric:interface.fabric">
382 </select>382 </select>
383 </div>383 </div>
384 <div class="table__data table__column--18">384 <div class="table__data table__column--18">
@@ -453,7 +453,7 @@
453 <select class="table__input" name="vlan" id="vlan"453 <select class="table__input" name="vlan" id="vlan"
454 data-ng-model="newInterface.vlan"454 data-ng-model="newInterface.vlan"
455 data-ng-disabled="newInterface.type == 'alias'"455 data-ng-disabled="newInterface.type == 'alias'"
456 data-ng-options="vlan as getVLANText(vlan) for vlan in vlans | filterByFabric:newInterface.parent.fabric | filterByUnusedForInterface:newInterface.parent:originalInterfaces"456 data-ng-options="vlan as getVLANText(vlan) for vlan in vlans | removeDefaultVLAN | filterByFabric:newInterface.parent.fabric | filterByUnusedForInterface:newInterface.parent:originalInterfaces"
457 data-ng-show="newInterface.type === 'vlan'"457 data-ng-show="newInterface.type === 'vlan'"
458 data-ng-change="addVLANChanged()">458 data-ng-change="addVLANChanged()">
459 </select>459 </select>
460460
=== modified file 'src/maasserver/testing/factory.py'
--- src/maasserver/testing/factory.py 2015-09-24 16:22:12 +0000
+++ src/maasserver/testing/factory.py 2015-10-09 21:26:30 +0000
@@ -709,8 +709,6 @@
709 return fannetwork709 return fannetwork
710710
711 def make_Fabric(self, name=None):711 def make_Fabric(self, name=None):
712 if name is None:
713 name = self.make_name('fabric')
714 fabric = Fabric(name=name)712 fabric = Fabric(name=name)
715 fabric.save()713 fabric.save()
716 return fabric714 return fabric
@@ -727,8 +725,6 @@
727725
728 def make_VLAN(self, name=None, vid=None, fabric=None):726 def make_VLAN(self, name=None, vid=None, fabric=None):
729 assert vid != 0, "VID=0 VLANs are auto-created"727 assert vid != 0, "VID=0 VLANs are auto-created"
730 if name is None:
731 name = self.make_name('vlan')
732 if fabric is None:728 if fabric is None:
733 fabric = Fabric.objects.get_default_fabric()729 fabric = Fabric.objects.get_default_fabric()
734 if vid is None:730 if vid is None:
735731
=== modified file 'src/maasserver/tests/test_forms_fabric.py'
--- src/maasserver/tests/test_forms_fabric.py 2015-08-13 19:29:08 +0000
+++ src/maasserver/tests/test_forms_fabric.py 2015-10-09 21:26:30 +0000
@@ -22,13 +22,6 @@
2222
23class TestFabricForm(MAASServerTestCase):23class TestFabricForm(MAASServerTestCase):
2424
25 def test__requires_name(self):
26 form = FabricForm({})
27 self.assertFalse(form.is_valid(), form.errors)
28 self.assertEquals({
29 "name": ["This field is required."]
30 }, form.errors)
31
32 def test__creates_fabric(self):25 def test__creates_fabric(self):
33 fabric_name = factory.make_name("fabric")26 fabric_name = factory.make_name("fabric")
34 form = FabricForm({27 form = FabricForm({
3528
=== modified file 'src/maasserver/tests/test_forms_space.py'
--- src/maasserver/tests/test_forms_space.py 2015-09-01 18:29:48 +0000
+++ src/maasserver/tests/test_forms_space.py 2015-10-09 21:26:30 +0000
@@ -39,13 +39,13 @@
39 self.assertEquals(space_name, space.name)39 self.assertEquals(space_name, space.name)
4040
41 def test__doest_require_name_on_update(self):41 def test__doest_require_name_on_update(self):
42 space = factory.make_Fabric()42 space = factory.make_Space()
43 form = SpaceForm(instance=space, data={})43 form = SpaceForm(instance=space, data={})
44 self.assertTrue(form.is_valid(), form.errors)44 self.assertTrue(form.is_valid(), form.errors)
4545
46 def test__updates_space(self):46 def test__updates_space(self):
47 new_name = factory.make_name("space")47 new_name = factory.make_name("space")
48 space = factory.make_Fabric()48 space = factory.make_Space()
49 form = SpaceForm(instance=space, data={49 form = SpaceForm(instance=space, data={
50 "name": new_name,50 "name": new_name,
51 })51 })
5252
=== modified file 'src/maasserver/tests/test_forms_vlan.py'
--- src/maasserver/tests/test_forms_vlan.py 2015-08-18 15:55:43 +0000
+++ src/maasserver/tests/test_forms_vlan.py 2015-10-09 21:26:30 +0000
@@ -24,12 +24,11 @@
2424
25class TestVLANForm(MAASServerTestCase):25class TestVLANForm(MAASServerTestCase):
2626
27 def test__requires_name(self):27 def test__requires_vid(self):
28 fabric = factory.make_Fabric()28 fabric = factory.make_Fabric()
29 form = VLANForm(fabric=fabric, data={})29 form = VLANForm(fabric=fabric, data={})
30 self.assertFalse(form.is_valid(), form.errors)30 self.assertFalse(form.is_valid(), form.errors)
31 self.assertEquals({31 self.assertEquals({
32 "name": ["This field is required."],
33 "vid": [32 "vid": [
34 "This field is required.",33 "This field is required.",
35 "Vid must be between 0 and 4095.",34 "Vid must be between 0 and 4095.",
@@ -55,6 +54,16 @@
55 form = VLANForm(instance=vlan, data={})54 form = VLANForm(instance=vlan, data={})
56 self.assertTrue(form.is_valid(), form.errors)55 self.assertTrue(form.is_valid(), form.errors)
5756
57 def test__cannot_edit_default_vlan(self):
58 fabric = factory.make_Fabric()
59 form = VLANForm(instance=fabric.get_default_vlan(), data={})
60 self.assertFalse(form.is_valid(), form.errors)
61 self.assertEquals({
62 "__all__": [
63 "Cannot modify the default VLAN for a fabric.",
64 ],
65 }, form.errors)
66
58 def test__updates_vlan(self):67 def test__updates_vlan(self):
59 vlan = factory.make_VLAN()68 vlan = factory.make_VLAN()
60 new_name = factory.make_name("vlan")69 new_name = factory.make_name("vlan")
6170
=== modified file 'src/maasserver/websockets/handlers/fabric.py'
--- src/maasserver/websockets/handlers/fabric.py 2015-10-04 22:28:33 +0000
+++ src/maasserver/websockets/handlers/fabric.py 2015-10-09 21:26:30 +0000
@@ -35,6 +35,7 @@
35 ]35 ]
3636
37 def dehydrate(self, obj, data, for_list=False):37 def dehydrate(self, obj, data, for_list=False):
38 data["name"] = obj.get_name()
38 data["vlan_ids"] = [39 data["vlan_ids"] = [
39 vlan.id40 vlan.id
40 for vlan in obj.vlan_set.all()41 for vlan in obj.vlan_set.all()
4142
=== modified file 'src/maasserver/websockets/handlers/tests/test_fabric.py'
--- src/maasserver/websockets/handlers/tests/test_fabric.py 2015-09-16 20:59:38 +0000
+++ src/maasserver/websockets/handlers/tests/test_fabric.py 2015-10-09 21:26:30 +0000
@@ -26,7 +26,7 @@
26 def dehydrate_fabric(self, fabric):26 def dehydrate_fabric(self, fabric):
27 data = {27 data = {
28 "id": fabric.id,28 "id": fabric.id,
29 "name": fabric.name,29 "name": fabric.get_name(),
30 "updated": dehydrate_datetime(fabric.updated),30 "updated": dehydrate_datetime(fabric.updated),
31 "created": dehydrate_datetime(fabric.created),31 "created": dehydrate_datetime(fabric.created),
32 "vlan_ids": [32 "vlan_ids": [
3333
=== modified file 'src/maasserver/websockets/handlers/tests/test_vlan.py'
--- src/maasserver/websockets/handlers/tests/test_vlan.py 2015-09-16 20:59:38 +0000
+++ src/maasserver/websockets/handlers/tests/test_vlan.py 2015-10-09 21:26:30 +0000
@@ -26,7 +26,7 @@
26 def dehydrate_vlan(self, vlan):26 def dehydrate_vlan(self, vlan):
27 data = {27 data = {
28 "id": vlan.id,28 "id": vlan.id,
29 "name": vlan.name,29 "name": vlan.get_name(),
30 "vid": vlan.vid,30 "vid": vlan.vid,
31 "fabric": vlan.fabric_id,31 "fabric": vlan.fabric_id,
32 "updated": dehydrate_datetime(vlan.updated),32 "updated": dehydrate_datetime(vlan.updated),
3333
=== modified file 'src/maasserver/websockets/handlers/vlan.py'
--- src/maasserver/websockets/handlers/vlan.py 2015-09-16 20:59:38 +0000
+++ src/maasserver/websockets/handlers/vlan.py 2015-10-09 21:26:30 +0000
@@ -36,6 +36,7 @@
36 ]36 ]
3737
38 def dehydrate(self, obj, data, for_list=False):38 def dehydrate(self, obj, data, for_list=False):
39 data["name"] = obj.get_name()
39 data["subnet_ids"] = [40 data["subnet_ids"] = [
40 subnet.id41 subnet.id
41 for subnet in obj.subnet_set.all()42 for subnet in obj.subnet_set.all()