Merge lp:~allenap/maas/dns-serials--bug-1571645--model into lp:~maas-committers/maas/trunk
- dns-serials--bug-1571645--model
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Gavin Panella | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 4962 | ||||
Proposed branch: | lp:~allenap/maas/dns-serials--bug-1571645--model | ||||
Merge into: | lp:~maas-committers/maas/trunk | ||||
Diff against target: |
348 lines (+203/-59) 4 files modified
src/maasserver/migrations/builtin/maasserver/0055_dns_publications.py (+28/-0) src/maasserver/models/__init__.py (+17/-59) src/maasserver/models/dnspublication.py (+74/-0) src/maasserver/models/tests/test_dnspublication.py (+84/-0) |
||||
To merge this branch: | bzr merge lp:~allenap/maas/dns-serials--bug-1571645--model | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Blake Rouse (community) | Approve | ||
Review via email: mp+292564@code.launchpad.net |
Commit message
New DNSPublication model to ensure that zone serials are consistent across an HA region.
Description of the change
This is the first part of addressing the linked bug, just a new model for system triggers to populate.
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~allenap/maas/dns-serials--bug-1571645--model into lp:maas failed. Below is the output from the failed tests.
Get:1 http://
Hit:2 http://
Get:3 http://
Hit:4 http://
Fetched 184 kB in 0s (384 kB/s)
Reading package lists...
sudo DEBIAN_
--no-
Reading package lists...
Building dependency tree...
Reading state information...
apache2 is already the newest version (2.4.18-2ubuntu3).
archdetect-deb is already the newest version (1.117ubuntu2).
authbind is already the newest version (2.1.1+nmu1).
bash is already the newest version (4.3-14ubuntu1).
bind9 is already the newest version (1:9.10.
bind9utils is already the newest version (1:9.10.
build-essential is already the newest version (12.1ubuntu2).
bzr is already the newest version (2.7.0-2ubuntu1).
curl is already the newest version (7.47.0-1ubuntu2).
debhelper is already the newest version (9.20160115ubun
distro-info is already the newest version (0.14build1).
dnsutils is already the newest version (1:9.10.
firefox is already the newest version (45.0.2+
freeipmi-tools is already the newest version (1.4.11-1ubuntu1).
git is already the newest version (1:2.7.4-0ubuntu1).
isc-dhcp-common is already the newest version (4.3.3-5ubuntu12).
libjs-angularjs is already the newest version (1.2.28-1ubuntu2).
libjs-jquery is already the newest version (1.11.3+dfsg-4).
libj...
MAAS Lander (maas-lander) wrote : | # |
There are additional revisions which have not been approved in review. Please seek review and approval of these new revisions.
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~allenap/maas/dns-serials--bug-1571645--model into lp:maas failed. Below is the output from the failed tests.
Get:1 http://
Hit:2 http://
Get:3 http://
Hit:4 http://
Fetched 184 kB in 0s (433 kB/s)
Reading package lists...
sudo DEBIAN_
--no-
Reading package lists...
Building dependency tree...
Reading state information...
apache2 is already the newest version (2.4.18-2ubuntu3).
archdetect-deb is already the newest version (1.117ubuntu2).
authbind is already the newest version (2.1.1+nmu1).
bash is already the newest version (4.3-14ubuntu1).
bind9 is already the newest version (1:9.10.
bind9utils is already the newest version (1:9.10.
build-essential is already the newest version (12.1ubuntu2).
bzr is already the newest version (2.7.0-2ubuntu1).
curl is already the newest version (7.47.0-1ubuntu2).
debhelper is already the newest version (9.20160115ubun
distro-info is already the newest version (0.14build1).
dnsutils is already the newest version (1:9.10.
firefox is already the newest version (45.0.2+
freeipmi-tools is already the newest version (1.4.11-1ubuntu1).
git is already the newest version (1:2.7.4-0ubuntu1).
isc-dhcp-common is already the newest version (4.3.3-5ubuntu12).
libjs-angularjs is already the newest version (1.2.28-1ubuntu2).
libjs-jquery is already the newest version (1.11.3+dfsg-4).
libj...
Gavin Panella (allenap) wrote : | # |
Spurious failure that lp:~allenap/maas/maas-crochet-run-test will help to address. For now, trying again.
Preview Diff
1 | === added file 'src/maasserver/migrations/builtin/maasserver/0055_dns_publications.py' |
2 | --- src/maasserver/migrations/builtin/maasserver/0055_dns_publications.py 1970-01-01 00:00:00 +0000 |
3 | +++ src/maasserver/migrations/builtin/maasserver/0055_dns_publications.py 2016-04-26 16:41:19 +0000 |
4 | @@ -0,0 +1,28 @@ |
5 | +# -*- coding: utf-8 -*- |
6 | +from __future__ import unicode_literals |
7 | + |
8 | +import django.core.validators |
9 | +from django.db import ( |
10 | + migrations, |
11 | + models, |
12 | +) |
13 | +import maasserver.models.dnspublication |
14 | + |
15 | + |
16 | +class Migration(migrations.Migration): |
17 | + |
18 | + dependencies = [ |
19 | + ('maasserver', '0054_controller'), |
20 | + ] |
21 | + |
22 | + operations = [ |
23 | + migrations.CreateModel( |
24 | + name='DNSPublication', |
25 | + fields=[ |
26 | + ('id', models.AutoField(auto_created=True, verbose_name='ID', serialize=False, primary_key=True)), |
27 | + ('serial', models.IntegerField(editable=False, default=maasserver.models.dnspublication.next_serial, validators=(django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(4294967295)))), |
28 | + ('created', models.DateTimeField(auto_now_add=True)), |
29 | + ('source', models.CharField(blank=True, editable=False, max_length=255, help_text='A brief explanation why DNS was published.')), |
30 | + ], |
31 | + ), |
32 | + ] |
33 | |
34 | === modified file 'src/maasserver/models/__init__.py' |
35 | --- src/maasserver/models/__init__.py 2016-04-15 22:14:33 +0000 |
36 | +++ src/maasserver/models/__init__.py 2016-04-26 16:41:19 +0000 |
37 | @@ -4,61 +4,69 @@ |
38 | """Model export and helpers for maasserver.""" |
39 | |
40 | __all__ = [ |
41 | - 'BMC', |
42 | 'Bcache', |
43 | 'BlockDevice', |
44 | + 'BMC', |
45 | + 'BondInterface', |
46 | 'BootResource', |
47 | 'BootResourceFile', |
48 | 'BootResourceSet', |
49 | 'BootSource', |
50 | 'BootSourceCache', |
51 | 'BootSourceSelection', |
52 | + 'BridgeInterface', |
53 | 'CacheSet', |
54 | 'ComponentError', |
55 | 'Config', |
56 | 'Controller', |
57 | + 'Device', |
58 | 'DHCPSnippet', |
59 | 'DNSData', |
60 | + 'DNSPublication', |
61 | 'DNSResource', |
62 | - 'Device', |
63 | 'Domain', |
64 | 'Event', |
65 | + 'EventType', |
66 | 'Fabric', |
67 | 'FanNetwork', |
68 | 'FileStorage', |
69 | 'Filesystem', |
70 | 'FilesystemGroup', |
71 | + 'Interface', |
72 | 'IPRange', |
73 | - 'Interface', |
74 | 'LargeFile', |
75 | 'LicenseKey', |
76 | + 'logger', |
77 | 'Machine', |
78 | 'Node', |
79 | + 'NodeGroupToRackController', |
80 | 'OwnerData', |
81 | 'Partition', |
82 | 'PartitionTable', |
83 | 'PhysicalBlockDevice', |
84 | 'PhysicalInterface', |
85 | + 'RackController', |
86 | 'RAID', |
87 | - 'RackController', |
88 | 'RegionController', |
89 | 'RegionControllerProcess', |
90 | 'RegionControllerProcessEndpoint', |
91 | 'RegionRackRPCConnection', |
92 | + 'Service', |
93 | + 'Space', |
94 | 'SSHKey', |
95 | 'SSLKey', |
96 | - 'Service', |
97 | - 'Space', |
98 | + 'StaticIPAddress', |
99 | 'Subnet', |
100 | 'Tag', |
101 | 'Template', |
102 | + 'UnknownInterface', |
103 | 'UserProfile', |
104 | - 'VLAN', |
105 | 'VersionedTextFile', |
106 | 'VirtualBlockDevice', |
107 | + 'VLAN', |
108 | + 'VLANInterface', |
109 | 'VolumeGroup', |
110 | 'Zone', |
111 | - 'logger', |
112 | ] |
113 | |
114 | from django.contrib.auth.backends import ModelBackend |
115 | @@ -87,6 +95,7 @@ |
116 | from maasserver.models.config import Config |
117 | from maasserver.models.dhcpsnippet import DHCPSnippet |
118 | from maasserver.models.dnsdata import DNSData |
119 | +from maasserver.models.dnspublication import DNSPublication |
120 | from maasserver.models.dnsresource import DNSResource |
121 | from maasserver.models.domain import Domain |
122 | from maasserver.models.event import Event |
123 | @@ -147,57 +156,6 @@ |
124 | from maasserver.utils import ignore_unused |
125 | from piston3.doc import HandlerDocumentation |
126 | |
127 | -# Suppress warning about symbols being imported, but only used for |
128 | -# export in __all__. |
129 | -ignore_unused( |
130 | - BMC, |
131 | - Bcache, |
132 | - BondInterface, |
133 | - BridgeInterface, |
134 | - BootResource, |
135 | - BootResourceFile, |
136 | - BootResourceSet, |
137 | - CacheSet, |
138 | - ComponentError, |
139 | - Config, |
140 | - Controller, |
141 | - DHCPSnippet, |
142 | - Event, |
143 | - EventType, |
144 | - Fabric, |
145 | - FileStorage, |
146 | - Filesystem, |
147 | - FilesystemGroup, |
148 | - IPRange, |
149 | - Interface, |
150 | - LargeFile, |
151 | - LicenseKey, |
152 | - NodeGroupToRackController, |
153 | - OwnerData, |
154 | - Partition, |
155 | - PartitionTable, |
156 | - RAID, |
157 | - RackController, |
158 | - RegionController, |
159 | - RegionControllerProcess, |
160 | - RegionControllerProcessEndpoint, |
161 | - SSHKey, |
162 | - Service, |
163 | - StaticIPAddress, |
164 | - Tag, |
165 | - Template, |
166 | - UnknownInterface, |
167 | - UserProfile, |
168 | - VLAN, |
169 | - VLANInterface, |
170 | - VersionedTextFile, |
171 | - VirtualBlockDevice, |
172 | - VolumeGroup, |
173 | - Zone, |
174 | - logger, |
175 | -) |
176 | - |
177 | - |
178 | # Connect the 'create_user' method to the post save signal of User. |
179 | post_save.connect(create_user, sender=User) |
180 | |
181 | |
182 | === added file 'src/maasserver/models/dnspublication.py' |
183 | --- src/maasserver/models/dnspublication.py 1970-01-01 00:00:00 +0000 |
184 | +++ src/maasserver/models/dnspublication.py 2016-04-26 16:41:19 +0000 |
185 | @@ -0,0 +1,74 @@ |
186 | +# Copyright 2016 Canonical Ltd. This software is licensed under the |
187 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
188 | + |
189 | +"""DNS publication model objects.""" |
190 | + |
191 | +__all__ = [ |
192 | + "DNSPublication", |
193 | +] |
194 | + |
195 | +from django.core.validators import ( |
196 | + MaxValueValidator, |
197 | + MinValueValidator, |
198 | +) |
199 | +from django.db.models import ( |
200 | + Manager, |
201 | + Model, |
202 | +) |
203 | +from django.db.models.fields import ( |
204 | + CharField, |
205 | + DateTimeField, |
206 | + IntegerField, |
207 | +) |
208 | +from maasserver import DefaultMeta |
209 | +from maasserver.dns.config import zone_serial |
210 | + |
211 | + |
212 | +def next_serial(): |
213 | + return next(zone_serial) |
214 | + |
215 | + |
216 | +class DNSPublicationManager(Manager): |
217 | + """Manager for DNS publishing records.""" |
218 | + |
219 | + def get_most_recent(self): |
220 | + """Return the most recently inserted `DNSPublication`.""" |
221 | + return self.order_by("-id")[0] |
222 | + |
223 | + def collect_garbage(self): |
224 | + """Delete all but the most recently inserted `DNSPublication`.""" |
225 | + self.filter(id__lt=self.get_most_recent().id).delete() |
226 | + |
227 | + |
228 | +class DNSPublication(Model): |
229 | + """A row in this table denotes a DNS publication request. |
230 | + |
231 | + Typically this will be populated by a trigger within the database. A |
232 | + listeners in regiond will be notified and consult the most recent record |
233 | + in this table. This way we can consistently publish zones with the same |
234 | + serial in an HA environment, and newly starting regiond processes can |
235 | + immediately be consistent with their peers. |
236 | + """ |
237 | + |
238 | + class Meta(DefaultMeta): |
239 | + """Needed for South to recognize this model.""" |
240 | + |
241 | + objects = DNSPublicationManager() |
242 | + |
243 | + # The serial number with which to publish the zone. We don't use the |
244 | + # primary key for this because zone serials are allowed to cycle. |
245 | + serial = IntegerField( |
246 | + editable=False, null=False, default=next_serial, unique=False, |
247 | + validators=( |
248 | + MinValueValidator(zone_serial.minvalue), |
249 | + MaxValueValidator(zone_serial.maxvalue), |
250 | + )) |
251 | + |
252 | + # This field is informational. |
253 | + created = DateTimeField( |
254 | + editable=False, null=False, auto_now=False, auto_now_add=True) |
255 | + |
256 | + # This field is informational. |
257 | + source = CharField( |
258 | + editable=False, max_length=255, null=False, blank=True, |
259 | + help_text="A brief explanation why DNS was published.") |
260 | |
261 | === added file 'src/maasserver/models/tests/test_dnspublication.py' |
262 | --- src/maasserver/models/tests/test_dnspublication.py 1970-01-01 00:00:00 +0000 |
263 | +++ src/maasserver/models/tests/test_dnspublication.py 2016-04-26 16:41:19 +0000 |
264 | @@ -0,0 +1,84 @@ |
265 | +# Copyright 2016 Canonical Ltd. This software is licensed under the |
266 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
267 | + |
268 | +"""Tests for general DNS models.""" |
269 | + |
270 | +__all__ = [] |
271 | + |
272 | +from datetime import ( |
273 | + datetime, |
274 | + timedelta, |
275 | +) |
276 | +from random import randint |
277 | + |
278 | +from maasserver.models.dnspublication import DNSPublication |
279 | +from maasserver.testing.factory import factory |
280 | +from maasserver.testing.testcase import MAASServerTestCase |
281 | +from testtools.matchers import ( |
282 | + Equals, |
283 | + HasLength, |
284 | + IsInstance, |
285 | + MatchesAll, |
286 | + MatchesStructure, |
287 | + Not, |
288 | +) |
289 | + |
290 | + |
291 | +class TestDNSPublication(MAASServerTestCase): |
292 | + """Test the `DNSPublication` model.""" |
293 | + |
294 | + def test_create_empty(self): |
295 | + pub = DNSPublication() |
296 | + pub.save() |
297 | + self.assertThat( |
298 | + pub, MatchesStructure( |
299 | + serial=IsInstance(int), |
300 | + created=IsInstance(datetime), |
301 | + source=Equals(""), |
302 | + )) |
303 | + |
304 | + def test_create_with_values(self): |
305 | + serial = randint(1, 5000) |
306 | + created = datetime.now() - timedelta(minutes=1098) |
307 | + source = factory.make_name("source") |
308 | + pub = DNSPublication(serial=serial, created=created, source=source) |
309 | + pub.save() |
310 | + self.assertThat( |
311 | + pub, MatchesStructure( |
312 | + serial=Equals(serial), |
313 | + created=MatchesAll( |
314 | + IsInstance(datetime), |
315 | + # `created` is always set; given values are ignored. |
316 | + Not(Equals(created)), |
317 | + first_only=True, |
318 | + ), |
319 | + source=Equals(source), |
320 | + )) |
321 | + |
322 | + |
323 | +class TestDNSPublicationManager(MAASServerTestCase): |
324 | + """Test `DNSPublicationManager`.""" |
325 | + |
326 | + def test_get_most_recent_returns_record_with_highest_id(self): |
327 | + DNSPublication(serial=3).save() |
328 | + DNSPublication(serial=30).save() |
329 | + DNSPublication(serial=10).save() |
330 | + self.assertThat( |
331 | + DNSPublication.objects.get_most_recent(), |
332 | + MatchesStructure(serial=Equals(10))) |
333 | + |
334 | + def test_get_most_recent_crashes_when_no_publications(self): |
335 | + # This is okay because we're going to ensure (using a migration) that |
336 | + # there is never less than one publication in the table. If this crash |
337 | + # happens we have bigger problems. |
338 | + self.assertRaises(IndexError, DNSPublication.objects.get_most_recent) |
339 | + |
340 | + def test_collect_garbage_removes_all_but_most_recent_record(self): |
341 | + for serial in range(10): |
342 | + DNSPublication(serial=serial).save() |
343 | + self.assertThat(DNSPublication.objects.all(), HasLength(10)) |
344 | + DNSPublication.objects.collect_garbage() |
345 | + self.assertThat(DNSPublication.objects.all(), HasLength(1)) |
346 | + self.assertThat( |
347 | + DNSPublication.objects.get_most_recent(), |
348 | + MatchesStructure(serial=Equals(serial))) |
Looks good.