Merge lp:~allenap/maas/ntp-vendor-data-split-servers-and-pools into lp:~maas-committers/maas/trunk

Proposed by Gavin Panella
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: 5443
Proposed branch: lp:~allenap/maas/ntp-vendor-data-split-servers-and-pools
Merge into: lp:~maas-committers/maas/trunk
Prerequisite: lp:~allenap/maas/ntp-rack-external-too
Diff against target: 197 lines (+72/-29)
4 files modified
src/metadataserver/tests/test_vendor_data.py (+12/-7)
src/metadataserver/vendor_data.py (+11/-3)
src/provisioningserver/ntp/config.py (+20/-19)
src/provisioningserver/ntp/tests/test_config.py (+29/-0)
To merge this branch: bzr merge lp:~allenap/maas/ntp-vendor-data-split-servers-and-pools
Reviewer Review Type Date Requested Status
Blake Rouse (community) Approve
Review via email: mp+307564@code.launchpad.net

Commit message

Publish both NTP servers and pools in vendor-data.

To post a comment you must log in.
Revision history for this message
Blake Rouse (blake-rouse) wrote :

Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/metadataserver/tests/test_vendor_data.py'
2--- src/metadataserver/tests/test_vendor_data.py 2016-10-05 06:13:22 +0000
3+++ src/metadataserver/tests/test_vendor_data.py 2016-10-05 06:13:22 +0000
4@@ -14,6 +14,7 @@
5 generate_system_info,
6 get_vendor_data,
7 )
8+from netaddr import IPAddress
9 from testtools.matchers import (
10 Contains,
11 ContainsDict,
12@@ -55,7 +56,8 @@
13 vendor_data = get_vendor_data(node)
14 self.assertThat(vendor_data, ContainsDict({
15 "ntp": Equals({
16- "servers": ["bar", "foo"],
17+ "servers": [],
18+ "pools": ["bar", "foo"],
19 }),
20 }))
21
22@@ -104,12 +106,15 @@
23
24 def test_external_only_yields_all_ntp_servers_when_defined(self):
25 Config.objects.set_config("ntp_external_only", True)
26- ntp_servers = factory.make_hostname(), factory.make_hostname()
27+ ntp_hosts = factory.make_hostname(), factory.make_hostname()
28+ ntp_addrs = factory.make_ipv4_address(), factory.make_ipv6_address()
29+ ntp_servers = ntp_hosts + ntp_addrs
30 Config.objects.set_config("ntp_servers", " ".join(ntp_servers))
31 configuration = generate_ntp_configuration(node=None)
32 self.assertThat(dict(configuration), Equals({
33 "ntp": {
34- "servers": sorted(ntp_servers),
35+ "servers": sorted(ntp_addrs, key=IPAddress),
36+ "pools": sorted(ntp_hosts),
37 },
38 }))
39
40@@ -148,9 +153,9 @@
41 configuration = generate_ntp_configuration(machine)
42 self.assertThat(dict(configuration), Equals({
43 "ntp": {
44- "servers": sorted((
45- rack_primary_address.ip,
46- rack_secondary_address.ip,
47- )),
48+ "servers": sorted(
49+ (rack_primary_address.ip, rack_secondary_address.ip),
50+ key=IPAddress),
51+ "pools": [],
52 },
53 }))
54
55=== modified file 'src/metadataserver/vendor_data.py'
56--- src/metadataserver/vendor_data.py 2016-10-05 06:13:22 +0000
57+++ src/metadataserver/vendor_data.py 2016-10-05 06:13:22 +0000
58@@ -10,6 +10,8 @@
59 from itertools import chain
60
61 from maasserver import ntp
62+from netaddr import IPAddress
63+from provisioningserver.ntp.config import normalise_address
64 from provisioningserver.utils.text import make_gecos_field
65
66
67@@ -47,9 +49,15 @@
68 - 102.10.10.10
69 - ntp.ubuntu.com
70
71- but MAAS does not yet distinguish between pool and non-pool servers, and
72- so this returns a single set of time references.
73+ MAAS assumes that IP addresses are "servers" and hostnames/FQDNs "pools".
74 """
75 ntp_servers = ntp.get_servers_for(node)
76 if len(ntp_servers) >= 1:
77- yield "ntp", {"servers": sorted(ntp_servers)}
78+ # Separate out IP addresses from the rest.
79+ addrs, other = set(), set()
80+ for ntp_server in map(normalise_address, ntp_servers):
81+ bucket = addrs if isinstance(ntp_server, IPAddress) else other
82+ bucket.add(ntp_server)
83+ servers = [addr.format() for addr in sorted(addrs)]
84+ pools = sorted(other) # Hostnames and FQDNs only.
85+ yield "ntp", {"servers": servers, "pools": pools}
86
87=== modified file 'src/provisioningserver/ntp/config.py'
88--- src/provisioningserver/ntp/config.py 2016-09-29 14:28:06 +0000
89+++ src/provisioningserver/ntp/config.py 2016-10-05 06:13:22 +0000
90@@ -6,6 +6,7 @@
91 __all__ = [
92 "configure_rack",
93 "configure_region",
94+ "normalise_address",
95 ]
96
97 from functools import partial
98@@ -58,6 +59,23 @@
99 configure_rack = partial(configure, offset=1)
100
101
102+def normalise_address(address):
103+ """Normalise an IP address into a form suitable for the `ntp` daemon.
104+
105+ It seems to prefer non-mapped IPv4 addresses, for example. Hostnames are
106+ passed through.
107+ """
108+ try:
109+ address = IPAddress(address)
110+ except AddrFormatError:
111+ return address # Hostname.
112+ else:
113+ if address.is_ipv4_mapped():
114+ return address.ipv4()
115+ else:
116+ return address
117+
118+
119 def _render_ntp_conf(includefile):
120 """Render ``ntp.conf`` based on the existing configuration.
121
122@@ -93,12 +111,12 @@
123 orphan mode (see http://support.ntp.org/bin/view/Support/OrphanMode).
124 """
125 lines = ["# MAAS NTP configuration."]
126- servers = map(_normalise_address, servers)
127+ servers = map(normalise_address, servers)
128 lines.extend(
129 "%s %s iburst" % (
130 ("server" if isinstance(server, IPAddress) else "pool"), server)
131 for server in servers)
132- peers = map(_normalise_address, peers)
133+ peers = map(normalise_address, peers)
134 lines.extend("peer %s" % peer for peer in peers)
135 lines.append("tos orphan {:d}".format(offset + 8))
136 lines.append("") # Add newline at end.
137@@ -168,20 +186,3 @@
138 if not blank:
139 yield from lines
140 yield "\n"
141-
142-
143-def _normalise_address(address):
144- """Normalise an IP address into a form suitable for the `ntp` daemon.
145-
146- It seems to prefer non-mapped IPv4 addresses, for example. Hostnames are
147- passed through.
148- """
149- try:
150- address = IPAddress(address)
151- except AddrFormatError:
152- return address # Hostname.
153- else:
154- if address.is_ipv4_mapped():
155- return address.ipv4()
156- else:
157- return address
158
159=== modified file 'src/provisioningserver/ntp/tests/test_config.py'
160--- src/provisioningserver/ntp/tests/test_config.py 2016-09-29 14:28:06 +0000
161+++ src/provisioningserver/ntp/tests/test_config.py 2016-10-05 06:13:22 +0000
162@@ -114,6 +114,35 @@
163 keywords=Equals({"offset": 1})))
164
165
166+class TestNormaliseAddress(MAASTestCase):
167+ """Tests for `p.ntp.config.normalise_address`."""
168+
169+ def test_returns_hostnames_unchanged(self):
170+ hostname = factory.make_hostname()
171+ self.assertThat(config.normalise_address(hostname), Equals(hostname))
172+
173+ def test_returns_ipv4_addresses_as_IPAddress(self):
174+ address = factory.make_ipv4_address()
175+ normalised = config.normalise_address(address)
176+ self.assertThat(normalised, IsInstance(IPAddress))
177+ self.assertThat(normalised.version, Equals(4))
178+ self.assertThat(str(normalised), Equals(address))
179+
180+ def test_returns_ipv6_addresses_as_IPAddress(self):
181+ address = factory.make_ipv6_address()
182+ normalised = config.normalise_address(address)
183+ self.assertThat(normalised, IsInstance(IPAddress))
184+ self.assertThat(normalised.version, Equals(6))
185+ self.assertThat(str(normalised), Equals(address))
186+
187+ def test_renders_ipv6_mapped_ipv4_addresses_as_plain_ipv4(self):
188+ address_as_ipv4 = factory.make_ipv4_address()
189+ address_as_ipv6 = str(IPAddress(address_as_ipv4).ipv6())
190+ normalised = config.normalise_address(address_as_ipv6)
191+ self.assertThat(normalised, IsInstance(IPAddress))
192+ self.assertThat(str(normalised), Equals(address_as_ipv4))
193+
194+
195 def extract_tos_options(configuration):
196 commands = re.findall(r"^ *tos +([^\r\n]+)$", configuration, re.MULTILINE)
197 return list(chain.from_iterable(map(str.split, commands)))