Merge lp:~rvb/maas/bug-1070765-hostname-1.2 into lp:maas/1.2

Proposed by Raphaël Badin on 2012-10-30
Status: Merged
Approved by: Raphaël Badin on 2012-10-30
Approved revision: 1281
Merged at revision: 1280
Proposed branch: lp:~rvb/maas/bug-1070765-hostname-1.2
Merge into: lp:maas/1.2
Diff against target: 507 lines (+236/-31)
11 files modified
src/maasserver/api.py (+19/-2)
src/maasserver/forms.py (+14/-1)
src/maasserver/models/dhcplease.py (+1/-5)
src/maasserver/models/node.py (+25/-2)
src/maasserver/models/nodegroup.py (+7/-5)
src/maasserver/tests/test_api.py (+107/-0)
src/maasserver/tests/test_dhcplease.py (+0/-15)
src/maasserver/tests/test_forms.py (+16/-1)
src/maasserver/tests/test_node.py (+26/-0)
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-1.2
Reviewer Review Type Date Requested Status
Raphaël Badin (community) Approve on 2012-10-30
Review via email: mp+132139@code.launchpad.net

Commit message

Backport [r1316..r1318].

Description of the change

Backport [r1316..r1318].

To post a comment you must log in.
Raphaël Badin (rvb) wrote :

Self-approving this as it is a straightforward backport.

review: Approve

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-30 10:25:55 +0000
+++ src/maasserver/api.py 2012-10-30 15:17:24 +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 (
@@ -474,6 +475,12 @@
474 model = Node475 model = Node
475 fields = DISPLAYED_NODE_FIELDS476 fields = DISPLAYED_NODE_FIELDS
476477
478 # Override the 'hostname' field so that it returns the FQDN instead as
479 # this is used by Juju to reach that node.
480 @classmethod
481 def hostname(handler, node):
482 return node.fqdn
483
477 def read(self, request, system_id):484 def read(self, request, system_id):
478 """Read a specific Node."""485 """Read a specific Node."""
479 return Node.objects.get_node_or_404(486 return Node.objects.get_node_or_404(
@@ -647,8 +654,15 @@
647class AnonNodesHandler(AnonymousOperationsHandler):654class AnonNodesHandler(AnonymousOperationsHandler):
648 """Create Nodes."""655 """Create Nodes."""
649 create = read = update = delete = None656 create = read = update = delete = None
657 model = Node
650 fields = DISPLAYED_NODE_FIELDS658 fields = DISPLAYED_NODE_FIELDS
651659
660 # Override the 'hostname' field so that it returns the FQDN instead as
661 # this is used by Juju to reach that node.
662 @classmethod
663 def hostname(handler, node):
664 return node.fqdn
665
652 @operation(idempotent=False)666 @operation(idempotent=False)
653 def new(self, request):667 def new(self, request):
654 """Create a new Node.668 """Create a new Node.
@@ -827,9 +841,12 @@
827 request.user, NODE_PERMISSION.VIEW, ids=match_ids)841 request.user, NODE_PERMISSION.VIEW, ids=match_ids)
828 if match_macs is not None:842 if match_macs is not None:
829 nodes = nodes.filter(macaddress__mac_address__in=match_macs)843 nodes = nodes.filter(macaddress__mac_address__in=match_macs)
830 # Prefetch related macaddresses and tags.844 # Prefetch related macaddresses, tags and nodegroups (plus
845 # related interfaces).
831 nodes = nodes.prefetch_related('macaddress_set__node')846 nodes = nodes.prefetch_related('macaddress_set__node')
832 nodes = nodes.prefetch_related('tags')847 nodes = nodes.prefetch_related('tags')
848 nodes = nodes.prefetch_related('nodegroup')
849 nodes = nodes.prefetch_related('nodegroup__nodegroupinterface_set')
833 return nodes.order_by('id')850 return nodes.order_by('id')
834851
835 @operation(idempotent=True)852 @operation(idempotent=True)
@@ -1708,7 +1725,7 @@
1708 preseed_url = compose_preseed_url(node)1725 preseed_url = compose_preseed_url(node)
1709 # The node's hostname may include a domain, but we ignore that1726 # The node's hostname may include a domain, but we ignore that
1710 # and use the one from the nodegroup instead.1727 # and use the one from the nodegroup instead.
1711 hostname = node.hostname.split('.', 1)[0]1728 hostname = strip_domain(node.hostname)
1712 domain = node.nodegroup.name1729 domain = node.nodegroup.name
1713 else:1730 else:
1714 try:1731 try:
17151732
=== modified file 'src/maasserver/forms.py'
--- src/maasserver/forms.py 2012-10-30 11:29:20 +0000
+++ src/maasserver/forms.py 2012-10-30 15:17:24 +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
@@ -82,6 +83,7 @@
82from maasserver.models.nodegroup import NODEGROUP_CLUSTER_NAME_TEMPLATE83from maasserver.models.nodegroup import NODEGROUP_CLUSTER_NAME_TEMPLATE
83from maasserver.node_action import compile_node_actions84from maasserver.node_action import compile_node_actions
84from maasserver.power_parameters import POWER_TYPE_PARAMETERS85from maasserver.power_parameters import POWER_TYPE_PARAMETERS
86from maasserver.utils import strip_domain
85from provisioningserver.enum import (87from provisioningserver.enum import (
86 POWER_TYPE,88 POWER_TYPE,
87 POWER_TYPE_CHOICES,89 POWER_TYPE_CHOICES,
@@ -334,6 +336,9 @@
334 node.nodegroup = form_value336 node.nodegroup = form_value
335337
336338
339IP_BASED_HOSTNAME_REGEXP = re.compile('\d{1,3}-\d{1,3}-\d{1,3}-\d{1,3}')
340
341
337class WithMACAddressesMixin:342class WithMACAddressesMixin:
338 """A form mixin which dynamically adds a MultipleMACAddressField to the343 """A form mixin which dynamically adds a MultipleMACAddressField to the
339 list of fields. This mixin also overrides the 'save' method to persist344 list of fields. This mixin also overrides the 'save' method to persist
@@ -389,7 +394,15 @@
389 node.save()394 node.save()
390 for mac in self.cleaned_data['mac_addresses']:395 for mac in self.cleaned_data['mac_addresses']:
391 node.add_mac_address(mac)396 node.add_mac_address(mac)
392 if self.cleaned_data['hostname'] == "":397 hostname = self.cleaned_data['hostname']
398 stripped_hostname = strip_domain(hostname)
399 # Generate a hostname for this node if the provided hostname is
400 # IP-based (because this means that this name comes from a DNS
401 # reverse query to the MAAS DNS) or an empty string.
402 generate_hostname = (
403 hostname == "" or
404 IP_BASED_HOSTNAME_REGEXP.match(stripped_hostname) != None)
405 if generate_hostname:
393 node.set_mac_based_hostname(self.cleaned_data['mac_addresses'][0])406 node.set_mac_based_hostname(self.cleaned_data['mac_addresses'][0])
394 return node407 return node
395408
396409
=== 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-30 15:17:24 +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/models/node.py'
--- src/maasserver/models/node.py 2012-10-26 10:20:19 +0000
+++ src/maasserver/models/node.py 2012-10-30 15:17:24 +0000
@@ -63,7 +63,10 @@
63from maasserver.models.dhcplease import DHCPLease63from maasserver.models.dhcplease import DHCPLease
64from maasserver.models.tag import Tag64from maasserver.models.tag import Tag
65from maasserver.models.timestampedmodel import TimestampedModel65from maasserver.models.timestampedmodel import TimestampedModel
66from maasserver.utils import get_db_state66from maasserver.utils import (
67 get_db_state,
68 strip_domain,
69 )
67from maasserver.utils.orm import get_first70from maasserver.utils.orm import get_first
68from piston.models import Token71from piston.models import Token
69from provisioningserver.enum import (72from provisioningserver.enum import (
@@ -487,10 +490,30 @@
487490
488 def __unicode__(self):491 def __unicode__(self):
489 if self.hostname:492 if self.hostname:
490 return "%s (%s)" % (self.system_id, self.hostname)493 return "%s (%s)" % (self.system_id, self.fqdn)
491 else:494 else:
492 return self.system_id495 return self.system_id
493496
497 @property
498 def fqdn(self):
499 """Fully qualified domain name for this node.
500
501 If MAAS manages DNS for this node, the domain part of the
502 hostname (if present), is replaced by the domain configured
503 on the cluster controller.
504 If not, simply return the node's hostname.
505 """
506 # Avoid circular imports.
507 from maasserver.dns import is_dns_managed
508 if is_dns_managed(self.nodegroup):
509 # If the hostname field contains a domain, strip it.
510 hostname = strip_domain(self.hostname)
511 # Build the FQDN by using the hostname and nodegroup.name
512 # as the domain name.
513 return '%s.%s' % (hostname, self.nodegroup.name)
514 else:
515 return self.hostname
516
494 def tag_names(self):517 def tag_names(self):
495 # We don't use self.tags.values_list here because this does not518 # We don't use self.tags.values_list here because this does not
496 # take advantage of the cache.519 # take advantage of the cache.
497520
=== modified file 'src/maasserver/models/nodegroup.py'
--- src/maasserver/models/nodegroup.py 2012-10-11 10:59:40 +0000
+++ src/maasserver/models/nodegroup.py 2012-10-30 15:17:24 +0000
@@ -31,7 +31,6 @@
31from maasserver.models.nodegroupinterface import NodeGroupInterface31from maasserver.models.nodegroupinterface import NodeGroupInterface
32from maasserver.models.timestampedmodel import TimestampedModel32from maasserver.models.timestampedmodel import TimestampedModel
33from maasserver.refresh_worker import refresh_worker33from maasserver.refresh_worker import refresh_worker
34from maasserver.utils.orm import get_one
35from piston.models import (34from piston.models import (
36 KEY_SIZE,35 KEY_SIZE,
37 Token,36 Token,
@@ -196,10 +195,13 @@
196 This is a temporary method that should be refactored once we add195 This is a temporary method that should be refactored once we add
197 proper support for multiple interfaces on a nodegroup.196 proper support for multiple interfaces on a nodegroup.
198 """197 """
199 return get_one(198 # Iterate over all the interfaces in python instead of doing the
200 NodeGroupInterface.objects.filter(199 # filtering in SQL so that this will use the cached version of
201 nodegroup=self).exclude(200 # self.nodegroupinterface_set if it is there.
202 management=NODEGROUPINTERFACE_MANAGEMENT.UNMANAGED))201 for interface in self.nodegroupinterface_set.all():
202 if interface.management != NODEGROUPINTERFACE_MANAGEMENT.UNMANAGED:
203 return interface
204 return None
203205
204 def ensure_dhcp_key(self):206 def ensure_dhcp_key(self):
205 """Ensure that this nodegroup has a dhcp key.207 """Ensure that this nodegroup has a dhcp key.
206208
=== modified file 'src/maasserver/tests/test_api.py'
--- src/maasserver/tests/test_api.py 2012-10-30 11:00:20 +0000
+++ src/maasserver/tests/test_api.py 2012-10-30 15:17:24 +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.
@@ -618,6 +637,93 @@
618 self.assertItemsEqual(['architecture'], parsed_result)637 self.assertItemsEqual(['architecture'], parsed_result)
619638
620639
640class NodeHostnameTest(APIv10TestMixin, MultipleUsersScenarios, TestCase):
641
642 scenarios = [
643 ('user', dict(userfactory=factory.make_user)),
644 ('admin', dict(userfactory=factory.make_admin)),
645 ]
646
647 def test_GET_list_returns_fqdn_with_domain_name_from_cluster(self):
648 # If DNS management is enabled, the domain part of a hostname
649 # is replaced by the domain name defined on the cluster.
650 hostname_without_domain = factory.make_name('hostname')
651 hostname_with_domain = '%s.%s' % (
652 hostname_without_domain, factory.getRandomString())
653 domain = factory.make_name('domain')
654 nodegroup = factory.make_node_group(
655 status=NODEGROUP_STATUS.ACCEPTED,
656 name=domain,
657 management=NODEGROUPINTERFACE_MANAGEMENT.DHCP_AND_DNS)
658 node = factory.make_node(
659 hostname=hostname_with_domain, nodegroup=nodegroup)
660 expected_hostname = '%s.%s' % (hostname_without_domain, domain)
661 response = self.client.get(self.get_uri('nodes/'), {'op': 'list'})
662 self.assertEqual(httplib.OK, response.status_code, response.content)
663 parsed_result = json.loads(response.content)
664 self.assertItemsEqual(
665 [expected_hostname],
666 [node.get('hostname') for node in parsed_result])
667
668
669class NodeHostnameEnlistmentTest(APIv10TestMixin, MultipleUsersScenarios,
670 TestCase):
671
672 scenarios = [
673 ('anon', dict(userfactory=lambda: AnonymousUser())),
674 ('user', dict(userfactory=factory.make_user)),
675 ('admin', dict(userfactory=factory.make_admin)),
676 ]
677
678 def test_created_node_has_domain_from_cluster(self):
679 hostname_without_domain = factory.make_name('hostname')
680 hostname_with_domain = '%s.%s' % (
681 hostname_without_domain, factory.getRandomString())
682 domain = factory.make_name('domain')
683 factory.make_node_group(
684 status=NODEGROUP_STATUS.ACCEPTED,
685 name=domain,
686 management=NODEGROUPINTERFACE_MANAGEMENT.DHCP_AND_DNS)
687 response = self.client.post(
688 self.get_uri('nodes/'),
689 {
690 'op': 'new',
691 'hostname': hostname_with_domain,
692 'architecture': factory.getRandomChoice(ARCHITECTURE_CHOICES),
693 'after_commissioning_action':
694 NODE_AFTER_COMMISSIONING_ACTION.DEFAULT,
695 'mac_addresses': [factory.getRandomMACAddress()],
696 })
697 self.assertEqual(httplib.OK, response.status_code, response.content)
698 parsed_result = json.loads(response.content)
699 expected_hostname = '%s.%s' % (hostname_without_domain, domain)
700 self.assertEqual(
701 expected_hostname, parsed_result.get('hostname'))
702
703 def test_created_node_gets_domain_from_cluster_appended(self):
704 hostname_without_domain = factory.make_name('hostname')
705 domain = factory.make_name('domain')
706 factory.make_node_group(
707 status=NODEGROUP_STATUS.ACCEPTED,
708 name=domain,
709 management=NODEGROUPINTERFACE_MANAGEMENT.DHCP_AND_DNS)
710 response = self.client.post(
711 self.get_uri('nodes/'),
712 {
713 'op': 'new',
714 'hostname': hostname_without_domain,
715 'architecture': factory.getRandomChoice(ARCHITECTURE_CHOICES),
716 'after_commissioning_action':
717 NODE_AFTER_COMMISSIONING_ACTION.DEFAULT,
718 'mac_addresses': [factory.getRandomMACAddress()],
719 })
720 self.assertEqual(httplib.OK, response.status_code, response.content)
721 parsed_result = json.loads(response.content)
722 expected_hostname = '%s.%s' % (hostname_without_domain, domain)
723 self.assertEqual(
724 expected_hostname, parsed_result.get('hostname'))
725
726
621class NonAdminEnlistmentAPITest(APIv10TestMixin, MultipleUsersScenarios,727class NonAdminEnlistmentAPITest(APIv10TestMixin, MultipleUsersScenarios,
622 TestCase):728 TestCase):
623 # Enlistment tests for non-admin users.729 # Enlistment tests for non-admin users.
@@ -684,6 +790,7 @@
684 'netboot',790 'netboot',
685 'power_type',791 'power_type',
686 'tag_names',792 'tag_names',
793 'resource_uri',
687 ],794 ],
688 list(parsed_result))795 list(parsed_result))
689796
690797
=== 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-30 15:17:24 +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-30 10:46:58 +0000
+++ src/maasserver/tests/test_forms.py 2012-10-30 15:17:24 +0000
@@ -112,14 +112,17 @@
112 return query_dict112 return query_dict
113113
114 def make_params(self, mac_addresses=None, architecture=None,114 def make_params(self, mac_addresses=None, architecture=None,
115 nodegroup=None):115 hostname=None, nodegroup=None):
116 if mac_addresses is None:116 if mac_addresses is None:
117 mac_addresses = [factory.getRandomMACAddress()]117 mac_addresses = [factory.getRandomMACAddress()]
118 if architecture is None:118 if architecture is None:
119 architecture = factory.getRandomEnum(ARCHITECTURE)119 architecture = factory.getRandomEnum(ARCHITECTURE)
120 if hostname is None:
121 hostname = factory.make_name('hostname')
120 params = {122 params = {
121 'mac_addresses': mac_addresses,123 'mac_addresses': mac_addresses,
122 'architecture': architecture,124 'architecture': architecture,
125 'hostname': hostname,
123 }126 }
124 if nodegroup is not None:127 if nodegroup is not None:
125 params['nodegroup'] = nodegroup128 params['nodegroup'] = nodegroup
@@ -218,6 +221,18 @@
218 form.save()221 form.save()
219 self.assertEqual(original_nodegroup, reload_object(node).nodegroup)222 self.assertEqual(original_nodegroup, reload_object(node).nodegroup)
220223
224 def test_form_without_hostname_generates_hostname(self):
225 form = NodeWithMACAddressesForm(self.make_params(hostname=''))
226 node = form.save()
227 self.assertTrue(len(node.hostname) > 0)
228
229 def test_form_with_ip_based_hostname_generates_hostname(self):
230 ip_based_hostname = '192-168-12-10.domain'
231 form = NodeWithMACAddressesForm(
232 self.make_params(hostname=ip_based_hostname))
233 node = form.save()
234 self.assertNotEqual(ip_based_hostname, node.hostname)
235
221236
222class TestOptionForm(ConfigForm):237class TestOptionForm(ConfigForm):
223 field1 = forms.CharField(label="Field 1", max_length=10)238 field1 = forms.CharField(label="Field 1", max_length=10)
224239
=== modified file 'src/maasserver/tests/test_node.py'
--- src/maasserver/tests/test_node.py 2012-10-24 14:59:57 +0000
+++ src/maasserver/tests/test_node.py 2012-10-30 15:17:24 +0000
@@ -26,6 +26,8 @@
26 NODE_STATUS,26 NODE_STATUS,
27 NODE_STATUS_CHOICES,27 NODE_STATUS_CHOICES,
28 NODE_STATUS_CHOICES_DICT,28 NODE_STATUS_CHOICES_DICT,
29 NODEGROUP_STATUS,
30 NODEGROUPINTERFACE_MANAGEMENT,
29 )31 )
30from maasserver.exceptions import NodeStateViolation32from maasserver.exceptions import NodeStateViolation
31from maasserver.models import (33from maasserver.models import (
@@ -570,6 +572,30 @@
570 node = reload_object(node)572 node = reload_object(node)
571 self.assertEqual([], list(node.tags.all()))573 self.assertEqual([], list(node.tags.all()))
572574
575 def test_fqdn_returns_hostname_if_dns_not_managed(self):
576 nodegroup = factory.make_node_group(
577 name=factory.getRandomString(),
578 management=NODEGROUPINTERFACE_MANAGEMENT.DHCP)
579 hostname_with_domain = '%s.%s' % (
580 factory.getRandomString(), factory.getRandomString())
581 node = factory.make_node(
582 nodegroup=nodegroup, hostname=hostname_with_domain)
583 self.assertEqual(hostname_with_domain, node.fqdn)
584
585 def test_fqdn_replaces_hostname_if_dns_is_managed(self):
586 hostname_without_domain = factory.make_name('hostname')
587 hostname_with_domain = '%s.%s' % (
588 hostname_without_domain, factory.getRandomString())
589 domain = factory.make_name('domain')
590 nodegroup = factory.make_node_group(
591 status=NODEGROUP_STATUS.ACCEPTED,
592 name=domain,
593 management=NODEGROUPINTERFACE_MANAGEMENT.DHCP_AND_DNS)
594 node = factory.make_node(
595 hostname=hostname_with_domain, nodegroup=nodegroup)
596 expected_hostname = '%s.%s' % (hostname_without_domain, domain)
597 self.assertEqual(expected_hostname, node.fqdn)
598
573599
574class NodeTransitionsTests(TestCase):600class NodeTransitionsTests(TestCase):
575 """Test the structure of NODE_TRANSITIONS."""601 """Test the structure of NODE_TRANSITIONS."""
576602
=== 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-30 15:17:24 +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-30 15:17:24 +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))

Subscribers

People subscribed via source and target branches

to status/vote changes: