Merge lp:~blake-rouse/maas/fix-1495849 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: 4265
Proposed branch: lp:~blake-rouse/maas/fix-1495849
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 290 lines (+199/-9)
4 files modified
src/maasserver/api/subnets.py (+9/-2)
src/maasserver/api/tests/test_subnets.py (+0/-2)
src/maasserver/forms_subnet.py (+64/-2)
src/maasserver/tests/test_forms_subnet.py (+126/-3)
To merge this branch: bzr merge lp:~blake-rouse/maas/fix-1495849
Reviewer Review Type Date Requested Status
Andres Rodriguez (community) Approve
Review via email: mp+271141@code.launchpad.net

Commit message

Improve the subnet API to give better parameters to make creating subnets easier.

To post a comment you must log in.
Revision history for this message
Andres Rodriguez (andreserl) wrote :

lgtm!

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

The attempt to merge lp:~blake-rouse/maas/fix-1495849 into lp:maas failed. Below is the output from the failed tests.

Ign http://security.ubuntu.com trusty-security InRelease
Ign http://nova.clouds.archive.ubuntu.com trusty InRelease
Hit http://security.ubuntu.com trusty-security Release.gpg
Hit http://security.ubuntu.com trusty-security Release
Ign http://nova.clouds.archive.ubuntu.com trusty-updates InRelease
Hit http://nova.clouds.archive.ubuntu.com trusty Release.gpg
Get:1 http://nova.clouds.archive.ubuntu.com trusty-updates Release.gpg [933 B]
Hit http://nova.clouds.archive.ubuntu.com trusty Release
Hit http://security.ubuntu.com trusty-security/main Sources
Get:2 http://nova.clouds.archive.ubuntu.com trusty-updates Release [63.5 kB]
Hit http://security.ubuntu.com trusty-security/universe Sources
Hit http://nova.clouds.archive.ubuntu.com trusty/main Sources
Hit http://security.ubuntu.com trusty-security/main amd64 Packages
Hit http://security.ubuntu.com trusty-security/universe amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Sources
Hit http://security.ubuntu.com trusty-security/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty/main amd64 Packages
Hit http://security.ubuntu.com trusty-security/universe Translation-en
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
Get:3 http://nova.clouds.archive.ubuntu.com trusty-updates/main Sources [234 kB]
Get:4 http://nova.clouds.archive.ubuntu.com trusty-updates/universe Sources [135 kB]
Get:5 http://nova.clouds.archive.ubuntu.com trusty-updates/main amd64 Packages [619 kB]
Get:6 http://nova.clouds.archive.ubuntu.com trusty-updates/universe amd64 Packages [312 kB]
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/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,365 kB in 3s (438 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-...

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

The attempt to merge lp:~blake-rouse/maas/fix-1495849 into lp:maas failed. Below is the output from the failed tests.

Ign http://security.ubuntu.com trusty-security InRelease
Hit http://security.ubuntu.com trusty-security Release.gpg
Ign http://nova.clouds.archive.ubuntu.com trusty InRelease
Hit http://security.ubuntu.com trusty-security Release
Ign http://nova.clouds.archive.ubuntu.com trusty-updates InRelease
Hit http://nova.clouds.archive.ubuntu.com trusty Release.gpg
Get:1 http://nova.clouds.archive.ubuntu.com trusty-updates Release.gpg [933 B]
Hit http://nova.clouds.archive.ubuntu.com trusty Release
Get:2 http://nova.clouds.archive.ubuntu.com trusty-updates Release [63.5 kB]
Hit http://security.ubuntu.com trusty-security/main Sources
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://security.ubuntu.com trusty-security/universe Sources
Hit http://nova.clouds.archive.ubuntu.com trusty/universe amd64 Packages
Hit http://security.ubuntu.com trusty-security/main amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty/main Translation-en
Hit http://security.ubuntu.com trusty-security/universe amd64 Packages
Hit http://security.ubuntu.com trusty-security/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Translation-en
Hit http://security.ubuntu.com trusty-security/universe Translation-en
Get:3 http://nova.clouds.archive.ubuntu.com trusty-updates/main Sources [234 kB]
Get:4 http://nova.clouds.archive.ubuntu.com trusty-updates/universe Sources [135 kB]
Get:5 http://nova.clouds.archive.ubuntu.com trusty-updates/main amd64 Packages [619 kB]
Get:6 http://nova.clouds.archive.ubuntu.com trusty-updates/universe amd64 Packages [312 kB]
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/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,365 kB in 3s (404 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-...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/api/subnets.py'
2--- src/maasserver/api/subnets.py 2015-09-01 19:21:50 +0000
3+++ src/maasserver/api/subnets.py 2015-09-15 16:27:58 +0000
4@@ -55,8 +55,15 @@
5 """Create a subnet.
6
7 :param name: Name of the subnet.
8- :param vlan: VLAN this subnet belongs to.
9- :param space: Space this subnet is in.
10+ :param fabric: Fabric for the subnet. Defaults to the fabric the
11+ provided VLAN belongs to or defaults to the default fabric.
12+ :param vlan: VLAN this subnet belongs to. Defaults to the default
13+ VLAN for the provided fabric or defaults to the default VLAN in
14+ the default fabric.
15+ :param vid: VID of the VLAN this subnet belongs to. Only used when
16+ vlan is not provided. Picks the VLAN with this VID in the provided
17+ fabric or the default fabric if one is not given.
18+ :param space: Space this subnet is in. Defaults to the default space.
19 :param cidr: The network CIDR for this subnet.
20 :param gateway_ip: The gateway IP address for this subnet.
21 :param dns_servers: Comma-seperated list of DNS servers for this \
22
23=== modified file 'src/maasserver/api/tests/test_subnets.py'
24--- src/maasserver/api/tests/test_subnets.py 2015-09-02 02:56:11 +0000
25+++ src/maasserver/api/tests/test_subnets.py 2015-09-15 16:27:58 +0000
26@@ -111,8 +111,6 @@
27 self.assertEqual(
28 httplib.BAD_REQUEST, response.status_code, response.content)
29 self.assertEqual({
30- "vlan": ["This field is required."],
31- "space": ["This field is required."],
32 "cidr": ["This field is required."],
33 }, json.loads(response.content))
34
35
36=== modified file 'src/maasserver/forms_subnet.py'
37--- src/maasserver/forms_subnet.py 2015-09-02 02:56:11 +0000
38+++ src/maasserver/forms_subnet.py 2015-09-15 16:27:58 +0000
39@@ -18,19 +18,28 @@
40
41 from django import forms
42 from maasserver.forms import MAASModelForm
43+from maasserver.models.fabric import Fabric
44 from maasserver.models.space import Space
45 from maasserver.models.subnet import Subnet
46 from maasserver.models.vlan import VLAN
47+from maasserver.utils.forms import set_form_error
48+from maasserver.utils.orm import get_one
49
50
51 class SubnetForm(MAASModelForm):
52 """Subnet creation/edition form."""
53
54+ fabric = forms.ModelChoiceField(
55+ queryset=Fabric.objects.all(), required=False)
56+
57 vlan = forms.ModelChoiceField(
58- queryset=VLAN.objects.all(), required=True)
59+ queryset=VLAN.objects.all(), required=False)
60+
61+ vid = forms.IntegerField(
62+ min_value=0, max_value=4095, required=False)
63
64 space = forms.ModelChoiceField(
65- queryset=Space.objects.all(), required=True)
66+ queryset=Space.objects.all(), required=False)
67
68 class Meta:
69 model = Subnet
70@@ -49,6 +58,16 @@
71
72 def clean(self):
73 cleaned_data = super(SubnetForm, self).clean()
74+ cleaned_data = self._clean_name(cleaned_data)
75+ if self.instance.id is None:
76+ # We only allow the helpers when creating. When updating we require
77+ # the VLAN specifically. This is because we cannot make a correct
78+ # determination on what should be done in this case.
79+ cleaned_data = self._clean_vlan(cleaned_data)
80+ cleaned_data = self._clean_space(cleaned_data)
81+ return cleaned_data
82+
83+ def _clean_name(self, cleaned_data):
84 name = cleaned_data.get("name", None)
85 instance_name_and_cidr_match = (
86 self.instance.id is not None and
87@@ -62,3 +81,46 @@
88 # Update the subnet to have the same name as its cidr.
89 cleaned_data["name"] = cleaned_data["cidr"]
90 return cleaned_data
91+
92+ def _clean_vlan(self, cleaned_data):
93+ fabric = cleaned_data.get("fabric", None)
94+ vlan = cleaned_data.get("vlan", None)
95+ vid = cleaned_data.get("vid", None)
96+ if fabric is None and vlan is None:
97+ if not vid:
98+ cleaned_data["vlan"] = (
99+ Fabric.objects.get_default_fabric().get_default_vlan())
100+ else:
101+ vlan = get_one(
102+ VLAN.objects.filter(
103+ fabric=Fabric.objects.get_default_fabric(), vid=vid))
104+ if vlan is None:
105+ set_form_error(
106+ self, "vid",
107+ "No VLAN with vid %s in default fabric." % vid)
108+ else:
109+ cleaned_data["vlan"] = vlan
110+ elif fabric is not None:
111+ if vlan is None:
112+ if not vid:
113+ cleaned_data["vlan"] = fabric.get_default_vlan()
114+ else:
115+ vlan = get_one(VLAN.objects.filter(fabric=fabric, vid=vid))
116+ if vlan is None:
117+ set_form_error(
118+ self, "vid",
119+ "No VLAN with vid %s in fabric %s." % (
120+ vid, fabric))
121+ else:
122+ cleaned_data["vlan"] = vlan
123+ elif vlan.fabric_id != fabric.id:
124+ set_form_error(
125+ self, "vlan",
126+ "VLAN %s is not in fabric %s." % (vlan, fabric))
127+ return cleaned_data
128+
129+ def _clean_space(self, cleaned_data):
130+ space = cleaned_data.get("space", None)
131+ if space is None:
132+ cleaned_data["space"] = Space.objects.get_default_space()
133+ return cleaned_data
134
135=== modified file 'src/maasserver/tests/test_forms_subnet.py'
136--- src/maasserver/tests/test_forms_subnet.py 2015-09-02 02:56:11 +0000
137+++ src/maasserver/tests/test_forms_subnet.py 2015-09-15 16:27:58 +0000
138@@ -15,6 +15,8 @@
139 __all__ = []
140
141 from maasserver.forms_subnet import SubnetForm
142+from maasserver.models.fabric import Fabric
143+from maasserver.models.space import Space
144 from maasserver.testing.factory import factory
145 from maasserver.testing.orm import reload_object
146 from maasserver.testing.testcase import MAASServerTestCase
147@@ -23,12 +25,10 @@
148
149 class TestSubnetForm(MAASServerTestCase):
150
151- def test__requires_vlan_space_cidr(self):
152+ def test__requires_cidr(self):
153 form = SubnetForm({})
154 self.assertFalse(form.is_valid(), form.errors)
155 self.assertEquals({
156- "vlan": ["This field is required."],
157- "space": ["This field is required."],
158 "cidr": ["This field is required."],
159 }, form.errors)
160
161@@ -75,6 +75,129 @@
162 subnet, MatchesStructure.byEquality(
163 name=cidr, vlan=vlan, space=space, cidr=cidr))
164
165+ def test__creates_subnet_in_default_space(self):
166+ vlan = factory.make_VLAN()
167+ network = factory.make_ip4_or_6_network()
168+ cidr = unicode(network.cidr)
169+ form = SubnetForm({
170+ "vlan": vlan.id,
171+ "cidr": cidr,
172+ })
173+ self.assertTrue(form.is_valid(), form.errors)
174+ subnet = form.save()
175+ self.assertThat(
176+ subnet, MatchesStructure.byEquality(
177+ name=cidr, vlan=vlan, cidr=cidr,
178+ space=Space.objects.get_default_space()))
179+
180+ def test__creates_subnet_in_default_fabric_and_vlan(self):
181+ network = factory.make_ip4_or_6_network()
182+ cidr = unicode(network.cidr)
183+ form = SubnetForm({
184+ "cidr": cidr,
185+ })
186+ self.assertTrue(form.is_valid(), form.errors)
187+ subnet = form.save()
188+ self.assertThat(
189+ subnet, MatchesStructure.byEquality(
190+ name=cidr, cidr=cidr,
191+ vlan=Fabric.objects.get_default_fabric().get_default_vlan(),
192+ space=Space.objects.get_default_space()))
193+
194+ def test__creates_subnet_in_default_vlan_in_fabric(self):
195+ fabric = factory.make_Fabric()
196+ network = factory.make_ip4_or_6_network()
197+ cidr = unicode(network.cidr)
198+ form = SubnetForm({
199+ "cidr": cidr,
200+ "fabric": fabric.id,
201+ })
202+ self.assertTrue(form.is_valid(), form.errors)
203+ subnet = form.save()
204+ self.assertThat(
205+ subnet, MatchesStructure.byEquality(
206+ name=cidr, cidr=cidr,
207+ vlan=fabric.get_default_vlan(),
208+ space=Space.objects.get_default_space()))
209+
210+ def test__creates_subnet_in_default_fabric_with_vid(self):
211+ vlan = factory.make_VLAN(fabric=Fabric.objects.get_default_fabric())
212+ network = factory.make_ip4_or_6_network()
213+ cidr = unicode(network.cidr)
214+ form = SubnetForm({
215+ "cidr": cidr,
216+ "vid": vlan.vid,
217+ })
218+ self.assertTrue(form.is_valid(), form.errors)
219+ subnet = form.save()
220+ self.assertThat(
221+ subnet, MatchesStructure.byEquality(
222+ name=cidr, cidr=cidr,
223+ vlan=vlan,
224+ space=Space.objects.get_default_space()))
225+
226+ def test__creates_subnet_in_fabric_with_vid(self):
227+ fabric = factory.make_Fabric()
228+ vlan = factory.make_VLAN(fabric=fabric)
229+ network = factory.make_ip4_or_6_network()
230+ cidr = unicode(network.cidr)
231+ form = SubnetForm({
232+ "cidr": cidr,
233+ "fabric": fabric.id,
234+ "vid": vlan.vid,
235+ })
236+ self.assertTrue(form.is_valid(), form.errors)
237+ subnet = form.save()
238+ self.assertThat(
239+ subnet, MatchesStructure.byEquality(
240+ name=cidr, cidr=cidr,
241+ vlan=vlan,
242+ space=Space.objects.get_default_space()))
243+
244+ def test__error_for_unknown_vid_in_default_fabric(self):
245+ fabric = factory.make_Fabric()
246+ vlan = factory.make_VLAN(fabric=fabric)
247+ network = factory.make_ip4_or_6_network()
248+ cidr = unicode(network.cidr)
249+ form = SubnetForm({
250+ "cidr": cidr,
251+ "vid": vlan.vid,
252+ })
253+ self.assertFalse(form.is_valid(), form.errors)
254+ self.assertEquals({
255+ "vid": ["No VLAN with vid %s in default fabric." % vlan.vid]
256+ }, form.errors)
257+
258+ def test__error_for_unknown_vid_in_fabric(self):
259+ fabric = factory.make_Fabric()
260+ vlan = factory.make_VLAN(fabric=Fabric.objects.get_default_fabric())
261+ network = factory.make_ip4_or_6_network()
262+ cidr = unicode(network.cidr)
263+ form = SubnetForm({
264+ "cidr": cidr,
265+ "fabric": fabric.id,
266+ "vid": vlan.vid,
267+ })
268+ self.assertFalse(form.is_valid(), form.errors)
269+ self.assertEquals({
270+ "vid": ["No VLAN with vid %s in fabric %s." % (vlan.vid, fabric)]
271+ }, form.errors)
272+
273+ def test__error_for_vlan_not_in_fabric(self):
274+ fabric = factory.make_Fabric()
275+ vlan = factory.make_VLAN(fabric=Fabric.objects.get_default_fabric())
276+ network = factory.make_ip4_or_6_network()
277+ cidr = unicode(network.cidr)
278+ form = SubnetForm({
279+ "cidr": cidr,
280+ "fabric": fabric.id,
281+ "vlan": vlan.id,
282+ })
283+ self.assertFalse(form.is_valid(), form.errors)
284+ self.assertEquals({
285+ "vlan": ["VLAN %s is not in fabric %s." % (vlan, fabric)]
286+ }, form.errors)
287+
288 def test__doest_require_vlan_space_or_cidr_on_update(self):
289 subnet = factory.make_Subnet()
290 form = SubnetForm(instance=subnet, data={})