Merge lp:~rvb/maas/maas-forwarders into lp:~maas-committers/maas/trunk

Proposed by Raphaël Badin
Status: Merged
Approved by: Raphaël Badin
Approved revision: no longer in the source branch.
Merged at revision: 4062
Proposed branch: lp:~rvb/maas/maas-forwarders
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 176 lines (+77/-6)
5 files modified
src/maasserver/dns/tests/test_config.py (+4/-3)
src/maasserver/fields.py (+25/-1)
src/maasserver/forms_settings.py (+3/-2)
src/maasserver/tests/test_fields.py (+37/-0)
src/maasserver/tests/test_forms_settings.py (+8/-0)
To merge this branch: bzr merge lp:~rvb/maas/maas-forwarders
Reviewer Review Type Date Requested Status
Mike Pontillo (community) Approve
Review via email: mp+263557@code.launchpad.net

Commit message

upstream_dns: accept a list of forwarders (and not just *one* forwarder).

Description of the change

This changes only the config form, the backend (in trunk) already supports space-separated IP addresses.
Tested in my lab.

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

Looks good to me. Thanks for fixing this!

review: Approve
Revision history for this message
Christian Reis (kiko) wrote :

On Wed, Jul 01, 2015 at 05:36:25PM -0000, Raphaël Badin wrote:
> + for ip in value.split():

Could we take commas here as well, just to be more accepting?
--
Christian Robottom Reis | [+1] 612 888 4935 | http://launchpad.net/~kiko
Canonical VP Hyperscale | [+55 16] 9 9112 6430

Revision history for this message
Mike Pontillo (mpontillo) wrote :

Taking commas would require further changes to the back-end code, since elsewhere we assume it is whitespace-separated.

Revision history for this message
Raphaël Badin (rvb) wrote :

> Taking commas would require further changes to the back-end code, since
> elsewhere we assume it is whitespace-separated.

Actually, it's not that big of a deal. Done.

Revision history for this message
Mike Pontillo (mpontillo) wrote :

I think I'd prefer a utility method rather than a pre-compiled UPSTREAM_DNS_SEPS, since that code is duplicated in a couple places. But looks good to me.

review: Approve
Revision history for this message
Raphaël Badin (rvb) wrote :

> I think I'd prefer a utility method rather than a pre-compiled
> UPSTREAM_DNS_SEPS, since that code is duplicated in a couple places. But looks
> good to me.

Yeah, I've fixed that by normalizing the list.

Revision history for this message
Mike Pontillo (mpontillo) wrote :

Nice; thanks for the fixes.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/dns/tests/test_config.py'
2--- src/maasserver/dns/tests/test_config.py 2015-06-15 12:42:07 +0000
3+++ src/maasserver/dns/tests/test_config.py 2015-07-02 08:37:08 +0000
4@@ -676,12 +676,13 @@
5 self.assertEqual([], get_upstream_dns())
6
7 def test__returns_list_of_one_address_if_set(self):
8- address = factory.make_ipv4_address()
9+ address = factory.make_ip_address()
10 Config.objects.set_config("upstream_dns", address)
11 self.assertEqual([address], get_upstream_dns())
12
13- def test__returns_list_of_many_address_if_set(self):
14- addresses = [factory.make_ipv4_address(), factory.make_ipv4_address()]
15+ def test__returns_list_if_space_separated_ips(self):
16+ addresses = [
17+ factory.make_ip_address() for _ in range(3)]
18 Config.objects.set_config("upstream_dns", " ".join(addresses))
19 self.assertEqual(addresses, get_upstream_dns())
20
21
22=== modified file 'src/maasserver/fields.py'
23--- src/maasserver/fields.py 2015-06-19 09:53:30 +0000
24+++ src/maasserver/fields.py 2015-07-02 08:37:08 +0000
25@@ -1,7 +1,7 @@
26 # Copyright 2012-2015 Canonical Ltd. This software is licensed under the
27 # GNU Affero General Public License version 3 (see the file LICENSE).
28
29-"""Custom model fields."""
30+"""Custom model and form fields."""
31
32 from __future__ import (
33 absolute_import,
34@@ -15,6 +15,7 @@
35 __all__ = [
36 "CIDRField",
37 "EditableBinaryField",
38+ "IPListFormField",
39 "MAASIPAddressField",
40 "MAC",
41 "MACAddressField",
42@@ -576,3 +577,26 @@
43 }
44 defaults.update(kwargs)
45 return super(CIDRField, self).formfield(**defaults)
46+
47+
48+class IPListFormField(CharField):
49+ """Accepts a Space/comma separated list of IP addresses.
50+
51+ This field normalizes the list to a space-separated list.
52+ """
53+ separators = re.compile('[,\ ]')
54+
55+ def clean(self, value):
56+ if value is None:
57+ return None
58+ else:
59+ ips = re.split(self.separators, value)
60+ ips = [ip.strip() for ip in ips if ip.strip() != '']
61+ for ip in ips:
62+ try:
63+ GenericIPAddressField().clean(ip, model_instance=None)
64+ except ValidationError:
65+ raise ValidationError(
66+ "Invalid IP address: %s; Provide a list of "
67+ "space-separated IP addresses" % ip)
68+ return ' '.join(ips)
69
70=== modified file 'src/maasserver/forms_settings.py'
71--- src/maasserver/forms_settings.py 2015-06-04 14:31:33 +0000
72+++ src/maasserver/forms_settings.py 2015-07-02 08:37:08 +0000
73@@ -26,6 +26,7 @@
74 from django import forms
75 from django.core.exceptions import ValidationError
76 from maasserver.bootresources import IMPORT_RESOURCES_SERVICE_PERIOD
77+from maasserver.fields import IPListFormField
78 from maasserver.models.config import (
79 Config,
80 DEFAULT_OS,
81@@ -180,11 +181,11 @@
82 },
83 'upstream_dns': {
84 'default': None,
85- 'form': forms.GenericIPAddressField,
86+ 'form': IPListFormField,
87 'form_kwargs': {
88 'label': (
89 "Upstream DNS used to resolve domains not managed by this "
90- "MAAS"),
91+ "MAAS (space/comma-separated IP addresses)"),
92 'required': False,
93 'help_text': (
94 "Only used when MAAS is running its own DNS server. This "
95
96=== modified file 'src/maasserver/tests/test_fields.py'
97--- src/maasserver/tests/test_fields.py 2015-06-19 09:53:30 +0000
98+++ src/maasserver/tests/test_fields.py 2015-07-02 08:37:08 +0000
99@@ -16,6 +16,7 @@
100
101 import json
102 from random import randint
103+import re
104
105 from django.core import serializers
106 from django.core.exceptions import ValidationError
107@@ -27,6 +28,7 @@
108 from maasserver.enum import NODEGROUPINTERFACE_MANAGEMENT
109 from maasserver.fields import (
110 EditableBinaryField,
111+ IPListFormField,
112 LargeObjectField,
113 LargeObjectFile,
114 MAC,
115@@ -542,3 +544,38 @@
116 normalized_cidr = '192.0.2.0/24'
117 instance = CIDRTestModel.objects.create(cidr=cidr)
118 self.assertEqual(normalized_cidr, reload_object(instance).cidr)
119+
120+
121+class IPListFormFieldTest(MAASServerTestCase):
122+
123+ def test_accepts_none(self):
124+ self.assertIsNone(IPListFormField().clean(None))
125+
126+ def test_accepts_single_ip(self):
127+ ip = factory.make_ip_address()
128+ self.assertEquals(ip, IPListFormField().clean(ip))
129+
130+ def test_accepts_space_separated_ips(self):
131+ ips = [factory.make_ip_address() for _ in range(5)]
132+ input = ' '.join(ips)
133+ self.assertEquals(input, IPListFormField().clean(input))
134+
135+ def test_accepts_comma_separated_ips(self):
136+ ips = [factory.make_ip_address() for _ in range(5)]
137+ input = ','.join(ips)
138+ self.assertEquals(' '.join(ips), IPListFormField().clean(input))
139+
140+ def test_rejects_invalid_input(self):
141+ invalid = factory.make_name('invalid')
142+ input = ' '.join([factory.make_ip_address(), invalid])
143+ error = self.assertRaises(
144+ ValidationError, IPListFormField().clean, input)
145+ self.assertIn("Invalid IP address: %s" % invalid, error.message)
146+
147+ def test_separators_dont_conflict_with_ipv4_address(self):
148+ self.assertIsNone(re.search(
149+ IPListFormField.separators, factory.make_ipv4_address()))
150+
151+ def test_separators_dont_conflict_with_ipv6_address(self):
152+ self.assertIsNone(re.search(
153+ IPListFormField.separators, factory.make_ipv6_address()))
154
155=== modified file 'src/maasserver/tests/test_forms_settings.py'
156--- src/maasserver/tests/test_forms_settings.py 2015-05-07 18:14:38 +0000
157+++ src/maasserver/tests/test_forms_settings.py 2015-07-02 08:37:08 +0000
158@@ -14,6 +14,7 @@
159 __metaclass__ = type
160 __all__ = []
161
162+
163 from django import forms
164 from maasserver.forms_settings import (
165 CONFIG_ITEMS,
166@@ -70,3 +71,10 @@
167 compose_invalid_choice_text(
168 'commissioning_distro_series', field.choices),
169 field.error_messages['invalid_choice'])
170+
171+ def test_upstream_dns_accepts_ip_list(self):
172+ field = get_config_field('upstream_dns')
173+ ips1 = [factory.make_ip_address() for _ in range(3)]
174+ ips2 = [factory.make_ip_address() for _ in range(3)]
175+ input = ' '.join(ips1) + ' ' + ','.join(ips2)
176+ self.assertEqual(' '.join(ips1 + ips2), field.clean(input))