Merge lp:~jtv/maas/flatten-dnszoneconfig into lp:~maas-committers/maas/trunk

Proposed by Jeroen T. Vermeulen
Status: Merged
Approved by: Jeroen T. Vermeulen
Approved revision: no longer in the source branch.
Merged at revision: 1839
Proposed branch: lp:~jtv/maas/flatten-dnszoneconfig
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 223 lines (+27/-60)
3 files modified
src/maasserver/dns.py (+1/-2)
src/provisioningserver/dns/config.py (+17/-43)
src/provisioningserver/dns/tests/test_config.py (+9/-15)
To merge this branch: bzr merge lp:~jtv/maas/flatten-dnszoneconfig
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+202509@code.launchpad.net

Commit message

Collapse DNSConfigBase into DNSZoneConfigBase.

Description of the change

Now that DNSConfig is no longer in the family, DNSConfigBase and DNSZoneConfigBase no longer need to be separate classes. This branch combines them, and some of the complexity falls out along the way. To keep refactoring work light, I hard-coded the zone files' access permissions (we never used the property) and to keep a handle on data flows, I moved dns_ip down to DNSForwardZoneConfig (the other classes make no use of it). I believe, but am not entirely certain, that the "mapping" parameter could likewise move down into DNSForwardZoneConfig.

At this point the data flows within the hierarchy become much clearer. They could be shortened a bit further still by passing the mappings and context to the base-class constructor as parameters, instead of having the base class retrieve them by calling overridable methods on itself. Delegation may be more appropriate than a common base class. The constructors for the concrete classes might be a bit easier to grasp if they named their parameters instead of accepting *args and **kwargs. Finally, I suppose shortened_reversed_ip really belongs in DNSReverseZoneConfig.

Jeroen

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

Looks good. One thing to consider.

[1]

-        networks = kwargs.pop("networks", None)
-        self.networks = [] if networks is None else networks
+        self.networks = kwargs.pop('networks', [])

This subtly changes behaviour. If kwargs["networks"] is None, this will
set self.networks to None. The original would set it to []. I'm not sure
if it's important though.

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

I know, thanks.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/dns.py'
2--- src/maasserver/dns.py 2014-01-20 07:54:17 +0000
3+++ src/maasserver/dns.py 2014-01-21 16:57:51 +0000
4@@ -229,11 +229,10 @@
5 def _gen_reverse_zones(nodegroups, serial, mappings, networks):
6 """Generator of reverse zones, sorted by network."""
7 get_domain = lambda nodegroup: nodegroup.name
8- dns_ip = get_dns_server_address()
9 reverse_nodegroups = sorted(nodegroups, key=networks.get)
10 for nodegroup in reverse_nodegroups:
11 yield DNSReverseZoneConfig(
12- get_domain(nodegroup), serial=serial, dns_ip=dns_ip,
13+ get_domain(nodegroup), serial=serial,
14 mapping=mappings[nodegroup], network=networks[nodegroup])
15
16 def __iter__(self):
17
18=== modified file 'src/provisioningserver/dns/config.py'
19--- src/provisioningserver/dns/config.py 2014-01-21 13:21:42 +0000
20+++ src/provisioningserver/dns/config.py 2014-01-21 16:57:51 +0000
21@@ -23,6 +23,7 @@
22
23 from abc import (
24 ABCMeta,
25+ abstractmethod,
26 abstractproperty,
27 )
28 from contextlib import contextmanager
29@@ -231,36 +232,6 @@
30 raise
31
32
33-class DNSConfigBase:
34- __metaclass__ = ABCMeta
35-
36- @abstractproperty
37- def template_path(self):
38- """Return the full path of the template to be used."""
39-
40- @abstractproperty
41- def target_path(self):
42- """Return the full path of the target file to be written."""
43-
44- @property
45- def template_dir(self):
46- return locate_config(TEMPLATES_DIR)
47-
48- @property
49- def access_permissions(self):
50- """The access permissions for the config file."""
51- return 0644
52-
53- @property
54- def target_dir(self):
55- return conf.DNS_CONFIG_DIR
56-
57- def get_context(self):
58- """Dictionary containing parameters to be included in the
59- parameters used when rendering the template."""
60- return {}
61-
62-
63 class DNSConfig:
64 """A DNS configuration file.
65
66@@ -316,26 +287,24 @@
67 return '.'.join(imap(unicode, significant_octets))
68
69
70-class DNSZoneConfigBase(DNSConfigBase):
71+class DNSZoneConfigBase:
72 """Base class for zone writers."""
73
74+ __metaclass__ = ABCMeta
75+
76 template_file_name = 'zone.template'
77
78- def __init__(self, domain, serial=None, mapping=None, dns_ip=None):
79+ def __init__(self, domain, serial=None, mapping=None):
80 """
81 :param domain: The domain name of the forward zone.
82 :param serial: The serial to use in the zone file. This must increment
83 on each change.
84 :param mapping: A hostname:ip-address mapping for all known hosts in
85 the zone.
86- :param dns_ip: The IP address of the DNS server authoritative for this
87- zone.
88 """
89- super(DNSZoneConfigBase, self).__init__()
90 self.domain = domain
91 self.serial = serial
92 self.mapping = {} if mapping is None else mapping
93- self.dns_ip = dns_ip
94
95 @abstractproperty
96 def zone_name(self):
97@@ -343,13 +312,17 @@
98
99 @property
100 def template_path(self):
101- return os.path.join(self.template_dir, self.template_file_name)
102+ return locate_config(TEMPLATES_DIR, self.template_file_name)
103
104 @property
105 def target_path(self):
106 """Return the full path of the DNS zone file."""
107- return os.path.join(
108- self.target_dir, 'zone.%s' % self.zone_name)
109+ return compose_config_path('zone.%s' % self.zone_name)
110+
111+ @abstractmethod
112+ def get_context(self):
113+ """Dictionary containing parameters to be included in the
114+ parameters used when rendering the template."""
115
116 def write_config(self):
117 """Write out this DNS zone file.
118@@ -361,8 +334,7 @@
119 """
120 content = render_dns_template(self.template_path, self.get_context())
121 with report_missing_config_dir():
122- incremental_write(
123- content, self.target_path, mode=self.access_permissions)
124+ incremental_write(content, self.target_path, mode=0644)
125
126
127 class DNSForwardZoneConfig(DNSZoneConfigBase):
128@@ -373,9 +345,11 @@
129
130 :param networks: The networks that the mapping exists within.
131 :type networks: Sequence of :class:`netaddr.IPNetwork`
132+ :param dns_ip: The IP address of the DNS server authoritative for this
133+ zone.
134 """
135- networks = kwargs.pop("networks", None)
136- self.networks = [] if networks is None else networks
137+ self.networks = kwargs.pop('networks', [])
138+ self.dns_ip = kwargs.pop('dns_ip', None)
139 super(DNSForwardZoneConfig, self).__init__(*args, **kwargs)
140
141 @property
142
143=== modified file 'src/provisioningserver/dns/tests/test_config.py'
144--- src/provisioningserver/dns/tests/test_config.py 2014-01-21 13:13:53 +0000
145+++ src/provisioningserver/dns/tests/test_config.py 2014-01-21 16:57:51 +0000
146@@ -298,8 +298,8 @@
147
148 def test_write_config_DNSConfigDirectoryMissing_if_dir_missing(self):
149 dnsconfig = DNSConfig()
150- dir_name = factory.make_name('nonesuch')
151- self.patch(DNSConfig, 'target_dir', dir_name)
152+ dir_name = patch_dns_config_path(self)
153+ os.rmdir(dir_name)
154 self.assertRaises(DNSConfigDirectoryMissing, dnsconfig.write_config)
155
156 def test_write_config_errors_if_unexpected_exception(self):
157@@ -503,8 +503,7 @@
158 )
159
160 def test_writes_dns_zone_config(self):
161- target_dir = self.make_dir()
162- self.patch(DNSForwardZoneConfig, 'target_dir', target_dir)
163+ target_dir = patch_dns_config_path(self)
164 domain = factory.getRandomString()
165 hostname = factory.getRandomString()
166 network = factory.getRandomNetwork()
167@@ -523,8 +522,7 @@
168 ])))
169
170 def test_writes_dns_zone_config_with_NS_record(self):
171- target_dir = self.make_dir()
172- self.patch(DNSForwardZoneConfig, 'target_dir', target_dir)
173+ target_dir = patch_dns_config_path(self)
174 network = factory.getRandomNetwork()
175 dns_ip = factory.getRandomIPAddress()
176 dns_zone_config = DNSForwardZoneConfig(
177@@ -541,7 +539,7 @@
178 ])))
179
180 def test_config_file_is_world_readable(self):
181- self.patch(DNSForwardZoneConfig, 'target_dir', self.make_dir())
182+ patch_dns_config_path(self)
183 network = factory.getRandomNetwork()
184 dns_zone_config = DNSForwardZoneConfig(
185 factory.getRandomString(), serial=random.randint(1, 100),
186@@ -637,13 +635,11 @@
187 )
188
189 def test_writes_dns_zone_config_with_NS_record(self):
190- target_dir = self.make_dir()
191- self.patch(DNSReverseZoneConfig, 'target_dir', target_dir)
192+ target_dir = patch_dns_config_path(self)
193 network = factory.getRandomNetwork()
194- dns_ip = factory.getRandomIPAddress()
195 dns_zone_config = DNSReverseZoneConfig(
196 factory.getRandomString(), serial=random.randint(1, 100),
197- dns_ip=dns_ip, network=network)
198+ network=network)
199 dns_zone_config.write_config()
200 self.assertThat(
201 os.path.join(
202@@ -652,8 +648,7 @@
203 matcher=Contains('IN NS %s.' % dns_zone_config.domain)))
204
205 def test_writes_reverse_dns_zone_config(self):
206- target_dir = self.make_dir()
207- self.patch(DNSReverseZoneConfig, 'target_dir', target_dir)
208+ target_dir = patch_dns_config_path(self)
209 domain = factory.getRandomString()
210 network = IPNetwork('192.168.0.1/22')
211 dns_zone_config = DNSReverseZoneConfig(
212@@ -667,10 +662,9 @@
213 FileContains(matcher=expected))
214
215 def test_reverse_config_file_is_world_readable(self):
216- self.patch(DNSReverseZoneConfig, 'target_dir', self.make_dir())
217+ patch_dns_config_path(self)
218 dns_zone_config = DNSReverseZoneConfig(
219 factory.getRandomString(), serial=random.randint(1, 100),
220- dns_ip=factory.getRandomIPAddress(),
221 network=factory.getRandomNetwork())
222 dns_zone_config.write_config()
223 filepath = FilePath(dns_zone_config.target_path)