Merge lp:~rvb/maas/bug-1070765-hostname 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: 1316
Proposed branch: lp:~rvb/maas/bug-1070765-hostname
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 240 lines (+73/-23)
8 files modified
src/maasserver/api.py (+2/-1)
src/maasserver/forms.py (+14/-1)
src/maasserver/models/dhcplease.py (+1/-5)
src/maasserver/tests/test_api.py (+19/-0)
src/maasserver/tests/test_dhcplease.py (+0/-15)
src/maasserver/tests/test_forms.py (+16/-1)
src/maasserver/utils/__init__.py (+6/-0)
src/maasserver/utils/tests/test_utils.py (+15/-0)
To merge this branch: bzr merge lp:~rvb/maas/bug-1070765-hostname
Reviewer Review Type Date Requested Status
Jeroen T. Vermeulen (community) Approve
Review via email: mp+131539@code.launchpad.net

Commit message

Change the Node creation form to ignore the provided hostname if it looks like an ip-based hostname.

Description of the change

Change the Node creation form to ignore the provided hostname if it looks like an ip-based hostname.

I know that IP_BASED_HOSTNAME_REGEXP matches things like 999.999.999.999 (non-valid ip adresses) but I don't think it's a problem really.

Drive-by fix: move strip_domain in the utils package.

To post a comment you must log in.
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Typo in forms.py: “hode.” Also, same line (line 58 of the diff):

+ # Generate a hostname for this hode if provided the hostname is

I think right after “hode” it should say “if the provided hostname,” not “if provided the hostname.”

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

> Typo in forms.py: “hode.” Also, same line (line 58 of the diff):
>
> + # Generate a hostname for this hode if provided the hostname is
>
> I think right after “hode” it should say “if the provided hostname,” not “if
> provided the hostname.”

Thanks for spotting this, typos fixed.

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

One question: should the hostname regex perhaps contain the final dot that separates the hostname proper from the domain name? (If so, of course it needs to be escaped or it'll match any character).

Jeroen

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

> One question: should the hostname regex perhaps contain the final dot that
> separates the hostname proper from the domain name? (If so, of course it
> needs to be escaped or it'll match any character).

I don't think so, we want to store only the hostname in that field (in the long run, but we have to cope with the fact that existing installations will have a fqdn in there).

Revision history for this message
Julian Edwards (julian-edwards) wrote :

58 + # Generate a hostname for this node if the provided hostname is
59 + # IP-based or an empty string.

You need to say *why* you are doing this, or it will look rather confusing when you come back to look in a year!

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

> You need to say *why* you are doing this, or it will look rather confusing
> when you come back to look in a year!

Right, done.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/api.py'
--- src/maasserver/api.py 2012-10-26 10:56:06 +0000
+++ src/maasserver/api.py 2012-10-29 09:54:36 +0000
@@ -165,6 +165,7 @@
165from maasserver.utils import (165from maasserver.utils import (
166 absolute_reverse,166 absolute_reverse,
167 map_enum,167 map_enum,
168 strip_domain,
168 )169 )
169from maasserver.utils.orm import get_one170from maasserver.utils.orm import get_one
170from piston.handler import (171from piston.handler import (
@@ -1800,7 +1801,7 @@
1800 preseed_url = compose_preseed_url(node)1801 preseed_url = compose_preseed_url(node)
1801 # The node's hostname may include a domain, but we ignore that1802 # The node's hostname may include a domain, but we ignore that
1802 # and use the one from the nodegroup instead.1803 # and use the one from the nodegroup instead.
1803 hostname = node.hostname.split('.', 1)[0]1804 hostname = strip_domain(node.hostname)
1804 domain = node.nodegroup.name1805 domain = node.nodegroup.name
1805 else:1806 else:
1806 try:1807 try:
18071808
=== modified file 'src/maasserver/forms.py'
--- src/maasserver/forms.py 2012-10-24 16:07:00 +0000
+++ src/maasserver/forms.py 2012-10-29 09:54:36 +0000
@@ -31,6 +31,7 @@
3131
32import collections32import collections
33import json33import json
34import re
3435
35from django import forms36from django import forms
36from django.contrib import messages37from django.contrib import messages
@@ -80,6 +81,7 @@
80from maasserver.models.nodegroup import NODEGROUP_CLUSTER_NAME_TEMPLATE81from maasserver.models.nodegroup import NODEGROUP_CLUSTER_NAME_TEMPLATE
81from maasserver.node_action import compile_node_actions82from maasserver.node_action import compile_node_actions
82from maasserver.power_parameters import POWER_TYPE_PARAMETERS83from maasserver.power_parameters import POWER_TYPE_PARAMETERS
84from maasserver.utils import strip_domain
83from provisioningserver.enum import (85from provisioningserver.enum import (
84 POWER_TYPE,86 POWER_TYPE,
85 POWER_TYPE_CHOICES,87 POWER_TYPE_CHOICES,
@@ -319,6 +321,9 @@
319 node.nodegroup = form_value321 node.nodegroup = form_value
320322
321323
324IP_BASED_HOSTNAME_REGEXP = re.compile('\d{1,3}-\d{1,3}-\d{1,3}-\d{1,3}')
325
326
322class WithMACAddressesMixin:327class WithMACAddressesMixin:
323 """A form mixin which dynamically adds a MultipleMACAddressField to the328 """A form mixin which dynamically adds a MultipleMACAddressField to the
324 list of fields. This mixin also overrides the 'save' method to persist329 list of fields. This mixin also overrides the 'save' method to persist
@@ -374,7 +379,15 @@
374 node.save()379 node.save()
375 for mac in self.cleaned_data['mac_addresses']:380 for mac in self.cleaned_data['mac_addresses']:
376 node.add_mac_address(mac)381 node.add_mac_address(mac)
377 if self.cleaned_data['hostname'] == "":382 hostname = self.cleaned_data['hostname']
383 stripped_hostname = strip_domain(hostname)
384 # Generate a hostname for this node if the provided hostname is
385 # IP-based (because this means that this name comes from a DNS
386 # reverse query to the MAAS DNS) or an empty string.
387 generate_hostname = (
388 hostname == "" or
389 IP_BASED_HOSTNAME_REGEXP.match(stripped_hostname) != None)
390 if generate_hostname:
378 node.set_mac_based_hostname(self.cleaned_data['mac_addresses'][0])391 node.set_mac_based_hostname(self.cleaned_data['mac_addresses'][0])
379 return node392 return node
380393
381394
=== modified file 'src/maasserver/models/dhcplease.py'
--- src/maasserver/models/dhcplease.py 2012-10-09 10:30:10 +0000
+++ src/maasserver/models/dhcplease.py 2012-10-29 09:54:36 +0000
@@ -25,11 +25,7 @@
25from maasserver import DefaultMeta25from maasserver import DefaultMeta
26from maasserver.fields import MACAddressField26from maasserver.fields import MACAddressField
27from maasserver.models.cleansave import CleanSave27from maasserver.models.cleansave import CleanSave
2828from maasserver.utils import strip_domain
29
30def strip_domain(hostname):
31 """Return `hostname` with the domain part removed."""
32 return hostname.split('.', 1)[0]
3329
3430
35class DHCPLeaseManager(Manager):31class DHCPLeaseManager(Manager):
3632
=== modified file 'src/maasserver/tests/test_api.py'
--- src/maasserver/tests/test_api.py 2012-10-26 10:56:06 +0000
+++ src/maasserver/tests/test_api.py 2012-10-29 09:54:36 +0000
@@ -381,6 +381,25 @@
381 [diane] = Node.objects.filter(hostname='diane')381 [diane] = Node.objects.filter(hostname='diane')
382 self.assertEqual(architecture, diane.architecture)382 self.assertEqual(architecture, diane.architecture)
383383
384 def test_POST_new_generates_hostname_if_ip_based_hostname(self):
385 hostname = '192-168-5-19.domain'
386 response = self.client.post(
387 self.get_uri('nodes/'),
388 {
389 'op': 'new',
390 'hostname': hostname,
391 'architecture': factory.getRandomChoice(ARCHITECTURE_CHOICES),
392 'after_commissioning_action':
393 NODE_AFTER_COMMISSIONING_ACTION.DEFAULT,
394 'mac_addresses': [factory.getRandomMACAddress()],
395 })
396 parsed_result = json.loads(response.content)
397
398 self.assertEqual(httplib.OK, response.status_code)
399 system_id = parsed_result.get('system_id')
400 node = Node.objects.get(system_id=system_id)
401 self.assertNotEqual(hostname, node.hostname)
402
384 def test_POST_new_creates_node_with_power_parameters(self):403 def test_POST_new_creates_node_with_power_parameters(self):
385 # We're setting power parameters so we disable start_commissioning to404 # We're setting power parameters so we disable start_commissioning to
386 # prevent anything from attempting to issue power instructions.405 # prevent anything from attempting to issue power instructions.
387406
=== modified file 'src/maasserver/tests/test_dhcplease.py'
--- src/maasserver/tests/test_dhcplease.py 2012-10-09 10:30:10 +0000
+++ src/maasserver/tests/test_dhcplease.py 2012-10-29 09:54:36 +0000
@@ -14,7 +14,6 @@
1414
15from maasserver import dns15from maasserver import dns
16from maasserver.models import DHCPLease16from maasserver.models import DHCPLease
17from maasserver.models.dhcplease import strip_domain
18from maasserver.testing.factory import factory17from maasserver.testing.factory import factory
19from maasserver.testing.testcase import TestCase18from maasserver.testing.testcase import TestCase
20from maasserver.utils import ignore_unused19from maasserver.utils import ignore_unused
@@ -47,20 +46,6 @@
47 self.assertEqual(mac, lease.mac)46 self.assertEqual(mac, lease.mac)
4847
4948
50class TestUtitilies(TestCase):
51
52 def test_strip_domain(self):
53 input_and_results = [
54 ('name.domain', 'name'),
55 ('name', 'name'),
56 ('name.domain.what', 'name'),
57 ('name..domain', 'name'),
58 ]
59 inputs = [input for input, _ in input_and_results]
60 results = [result for _, result in input_and_results]
61 self.assertEqual(results, map(strip_domain, inputs))
62
63
64class TestDHCPLeaseManager(TestCase):49class TestDHCPLeaseManager(TestCase):
65 """Tests for :class:`DHCPLeaseManager`."""50 """Tests for :class:`DHCPLeaseManager`."""
6651
6752
=== modified file 'src/maasserver/tests/test_forms.py'
--- src/maasserver/tests/test_forms.py 2012-10-07 11:00:05 +0000
+++ src/maasserver/tests/test_forms.py 2012-10-29 09:54:36 +0000
@@ -111,14 +111,17 @@
111 return query_dict111 return query_dict
112112
113 def make_params(self, mac_addresses=None, architecture=None,113 def make_params(self, mac_addresses=None, architecture=None,
114 nodegroup=None):114 hostname=None, nodegroup=None):
115 if mac_addresses is None:115 if mac_addresses is None:
116 mac_addresses = [factory.getRandomMACAddress()]116 mac_addresses = [factory.getRandomMACAddress()]
117 if architecture is None:117 if architecture is None:
118 architecture = factory.getRandomEnum(ARCHITECTURE)118 architecture = factory.getRandomEnum(ARCHITECTURE)
119 if hostname is None:
120 hostname = factory.make_name('hostname')
119 params = {121 params = {
120 'mac_addresses': mac_addresses,122 'mac_addresses': mac_addresses,
121 'architecture': architecture,123 'architecture': architecture,
124 'hostname': hostname,
122 }125 }
123 if nodegroup is not None:126 if nodegroup is not None:
124 params['nodegroup'] = nodegroup127 params['nodegroup'] = nodegroup
@@ -217,6 +220,18 @@
217 form.save()220 form.save()
218 self.assertEqual(original_nodegroup, reload_object(node).nodegroup)221 self.assertEqual(original_nodegroup, reload_object(node).nodegroup)
219222
223 def test_form_without_hostname_generates_hostname(self):
224 form = NodeWithMACAddressesForm(self.make_params(hostname=''))
225 node = form.save()
226 self.assertTrue(len(node.hostname) > 0)
227
228 def test_form_with_ip_based_hostname_generates_hostname(self):
229 ip_based_hostname = '192-168-12-10.domain'
230 form = NodeWithMACAddressesForm(
231 self.make_params(hostname=ip_based_hostname))
232 node = form.save()
233 self.assertNotEqual(ip_based_hostname, node.hostname)
234
220235
221class TestOptionForm(ConfigForm):236class TestOptionForm(ConfigForm):
222 field1 = forms.CharField(label="Field 1", max_length=10)237 field1 = forms.CharField(label="Field 1", max_length=10)
223238
=== modified file 'src/maasserver/utils/__init__.py'
--- src/maasserver/utils/__init__.py 2012-08-24 10:28:29 +0000
+++ src/maasserver/utils/__init__.py 2012-10-29 09:54:36 +0000
@@ -15,6 +15,7 @@
15 'get_db_state',15 'get_db_state',
16 'ignore_unused',16 'ignore_unused',
17 'map_enum',17 'map_enum',
18 'strip_domain',
18 ]19 ]
1920
20from urllib import urlencode21from urllib import urlencode
@@ -82,3 +83,8 @@
82 if query is not None:83 if query is not None:
83 url += '?%s' % urlencode(query, doseq=True)84 url += '?%s' % urlencode(query, doseq=True)
84 return url85 return url
86
87
88def strip_domain(hostname):
89 """Return `hostname` with the domain part removed."""
90 return hostname.split('.', 1)[0]
8591
=== modified file 'src/maasserver/utils/tests/test_utils.py'
--- src/maasserver/utils/tests/test_utils.py 2012-06-26 16:36:10 +0000
+++ src/maasserver/utils/tests/test_utils.py 2012-10-29 09:54:36 +0000
@@ -23,6 +23,7 @@
23 absolute_reverse,23 absolute_reverse,
24 get_db_state,24 get_db_state,
25 map_enum,25 map_enum,
26 strip_domain,
26 )27 )
27from maastesting.testcase import TestCase28from maastesting.testcase import TestCase
2829
@@ -104,3 +105,17 @@
104 NODE_STATUS_CHOICES, but_not=[status])105 NODE_STATUS_CHOICES, but_not=[status])
105 node.status = another_status106 node.status = another_status
106 self.assertEqual(status, get_db_state(node, 'status'))107 self.assertEqual(status, get_db_state(node, 'status'))
108
109
110class TestStripDomain(TestCase):
111
112 def test_strip_domain(self):
113 input_and_results = [
114 ('name.domain', 'name'),
115 ('name', 'name'),
116 ('name.domain.what', 'name'),
117 ('name..domain', 'name'),
118 ]
119 inputs = [input for input, _ in input_and_results]
120 results = [result for _, result in input_and_results]
121 self.assertEqual(results, map(strip_domain, inputs))