Merge lp:~allenap/maas/dns-serials--bug-1571645 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: 4981
Proposed branch: lp:~allenap/maas/dns-serials--bug-1571645
Merge into: lp:~maas-committers/maas/trunk
Prerequisite: lp:~allenap/maas/dns-serials--bug-1571645--sequence
Diff against target: 1539 lines (+549/-189)
12 files modified
src/maasserver/api/tests/test_domains.py (+33/-13)
src/maasserver/dns/config.py (+3/-5)
src/maasserver/dns/tests/test_config.py (+25/-40)
src/maasserver/migrations/builtin/maasserver/0057_initial_dns_publication.py (+37/-0)
src/maasserver/migrations/builtin/maasserver/0058_bigger_integer_for_dns_publication_serial.py (+22/-0)
src/maasserver/migrations/builtin/maasserver/0059_merge.py (+13/-0)
src/maasserver/models/dnspublication.py (+19/-5)
src/maasserver/models/tests/test_dnspublication.py (+41/-5)
src/maasserver/triggers/system.py (+103/-31)
src/maasserver/triggers/testing.py (+49/-11)
src/maasserver/triggers/tests/test_system_listener.py (+203/-78)
src/maasserver/triggers/tests/test_websocket_listener.py (+1/-1)
To merge this branch: bzr merge lp:~allenap/maas/dns-serials--bug-1571645
Reviewer Review Type Date Requested Status
LaMont Jones (community) Approve
Mike Pontillo (community) Approve
Review via email: mp+293307@code.launchpad.net

Commit message

Drive DNS publication from the DNSPublication model.

Previously a sequence was being used directly for the zone serial. However, sequences are explicitly not transactional: in an HA environment, a zone published on one region host would not necessarily have the same serial as the same zone published on another host. Using a regular table ensures that all region processes use the same serial.

To post a comment you must log in.
Revision history for this message
Mike Pontillo (mpontillo) wrote :

I like this branch; well done. Looks like an elegant solution to the problem. I've commented below with some suggestions. Nothing blocking, though.

Can you add more detail in the commit message about why it's doing what it's doing?

You may need to reorder migrations; I think Blake has a migration, too.

Finally, it would be nice if LaMont reviews this, too.

review: Approve
Revision history for this message
Gavin Panella (allenap) wrote :

> I like this branch; well done. Looks like an elegant solution to the
> problem. I've commented below with some suggestions. Nothing blocking,
> though.

Thanks!

> Can you add more detail in the commit message about why it's doing what
> it's doing?

Sure; done.

> You may need to reorder migrations; I think Blake has a migration,
> too.

Ah, yes. Merged.

> Finally, it would be nice if LaMont reviews this, too.

I will prod him about it.

Revision history for this message
Gavin Panella (allenap) wrote :

LaMont, do you mind sanity checking this?

Revision history for this message
LaMont Jones (lamont) wrote :

A minor test question or two, but looks reasonable to me. No objections here.

review: Approve
Revision history for this message
Gavin Panella (allenap) wrote :

Thanks LaMont!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/api/tests/test_domains.py'
--- src/maasserver/api/tests/test_domains.py 2016-04-29 11:37:21 +0000
+++ src/maasserver/api/tests/test_domains.py 2016-04-29 11:37:21 +0000
@@ -99,19 +99,39 @@
99 'serial': str(serial)})99 'serial': str(serial)})
100 self.assertEqual(100 self.assertEqual(
101 http.client.OK, response.status_code, response.content)101 http.client.OK, response.status_code, response.content)
102 self.assertEqual(serial, next(zone_serial))102 # The handler forces a DNS reload by creating a new DNS publication,
103103 # so the serial has already been incremented.
104 def test_set_serial_detects_bad_values(self):104 self.assertEqual(serial + 1, next(zone_serial))
105 zone_serial.create_if_not_exists()105
106 self.become_admin()106 def test_set_serial_rejects_serials_less_than_1(self):
107 uri = get_domains_uri()107 zone_serial.create_if_not_exists()
108 serial = random.randint(1, INT_MAX)108 self.become_admin()
109 response = self.client.post(uri, {109 uri = get_domains_uri()
110 'op': 'set_serial',110 # A serial of 1 is fine.
111 'serial': str(serial)})111 response = self.client.post(
112 self.assertEqual(112 uri, {'op': 'set_serial', 'serial': '1'})
113 http.client.OK, response.status_code, response.content)113 self.assertEqual(
114 self.assertEqual(serial, next(zone_serial))114 http.client.OK, response.status_code, response.content)
115 # A serial of 0 is rejected.
116 response = self.client.post(
117 uri, {'op': 'set_serial', 'serial': '0'})
118 self.assertEqual(
119 http.client.BAD_REQUEST, response.status_code, response.content)
120
121 def test_set_serial_rejects_serials_greater_than_4294967295(self):
122 zone_serial.create_if_not_exists()
123 self.become_admin()
124 uri = get_domains_uri()
125 # A serial of 4294967295 is fine.
126 response = self.client.post(
127 uri, {'op': 'set_serial', 'serial': '4294967295'})
128 self.assertEqual(
129 http.client.OK, response.status_code, response.content)
130 # A serial of 4294967296 is rejected.
131 response = self.client.post(
132 uri, {'op': 'set_serial', 'serial': '4294967296'})
133 self.assertEqual(
134 http.client.BAD_REQUEST, response.status_code, response.content)
115135
116136
117class TestDomainAPI(APITestCase):137class TestDomainAPI(APITestCase):
118138
=== modified file 'src/maasserver/dns/config.py'
--- src/maasserver/dns/config.py 2016-04-29 11:37:21 +0000
+++ src/maasserver/dns/config.py 2016-04-29 11:37:21 +0000
@@ -9,11 +9,10 @@
9 ]9 ]
1010
11from django.conf import settings11from django.conf import settings
12from django.db import connection
13from maasserver.dns.zonegenerator import ZoneGenerator12from maasserver.dns.zonegenerator import ZoneGenerator
14from maasserver.enum import RDNS_MODE13from maasserver.enum import RDNS_MODE
15from maasserver.models.config import Config14from maasserver.models.config import Config
16from maasserver.models.dnspublication import zone_serial15from maasserver.models.dnspublication import DNSPublication
17from maasserver.models.domain import Domain16from maasserver.models.domain import Domain
18from maasserver.models.subnet import Subnet17from maasserver.models.subnet import Subnet
19from provisioningserver.dns.actions import (18from provisioningserver.dns.actions import (
@@ -30,7 +29,7 @@
3029
3130
32def current_zone_serial():31def current_zone_serial():
33 return '%0.10d' % zone_serial.current()32 return '%0.10d' % DNSPublication.objects.get_most_recent().serial
3433
3534
36def is_dns_enabled():35def is_dns_enabled():
@@ -40,8 +39,7 @@
4039
41def dns_force_reload():40def dns_force_reload():
42 """Force the DNS to be regenerated."""41 """Force the DNS to be regenerated."""
43 cursor = connection.cursor()42 DNSPublication(source="Force reload").save()
44 cursor.execute("SELECT pg_notify('sys_dns', '')")
4543
4644
47def dns_update_all_zones(reload_retry=False):45def dns_update_all_zones(reload_retry=False):
4846
=== modified file 'src/maasserver/dns/tests/test_config.py'
--- src/maasserver/dns/tests/test_config.py 2016-03-31 23:34:55 +0000
+++ src/maasserver/dns/tests/test_config.py 2016-04-29 11:37:21 +0000
@@ -8,7 +8,6 @@
8import random8import random
9import time9import time
1010
11from crochet import wait_for
12from django.conf import settings11from django.conf import settings
13from django.core.management import call_command12from django.core.management import call_command
14import dns.resolver13import dns.resolver
@@ -20,7 +19,6 @@
20 dns_update_all_zones,19 dns_update_all_zones,
21 get_trusted_networks,20 get_trusted_networks,
22 get_upstream_dns,21 get_upstream_dns,
23 zone_serial,
24)22)
25from maasserver.enum import (23from maasserver.enum import (
26 IPADDRESS_TYPE,24 IPADDRESS_TYPE,
@@ -31,11 +29,10 @@
31 Config,29 Config,
32 Domain,30 Domain,
33)31)
32from maasserver.models.dnspublication import DNSPublication
34from maasserver.testing.config import RegionConfigurationFixture33from maasserver.testing.config import RegionConfigurationFixture
35from maasserver.testing.factory import factory34from maasserver.testing.factory import factory
36from maasserver.testing.testcase import MAASServerTestCase35from maasserver.testing.testcase import MAASServerTestCase
37from maasserver.utils.orm import transactional
38from maasserver.utils.threads import deferToDatabase
39from maastesting.matchers import MockCalledOnceWith36from maastesting.matchers import MockCalledOnceWith
40from netaddr import IPAddress37from netaddr import IPAddress
41from provisioningserver.dns.config import (38from provisioningserver.dns.config import (
@@ -51,16 +48,13 @@
51 BINDServer,48 BINDServer,
52)49)
53from provisioningserver.testing.tests.test_bindfixture import dig_call50from provisioningserver.testing.tests.test_bindfixture import dig_call
54from provisioningserver.utils.twisted import (51from provisioningserver.utils.twisted import retries
55 DeferredValue,
56 retries,
57)
58from testtools.matchers import (52from testtools.matchers import (
59 Contains,53 Contains,
54 Equals,
60 FileContains,55 FileContains,
61 MatchesStructure,56 MatchesStructure,
62)57)
63from twisted.internet.defer import inlineCallbacks
6458
6559
66class TestDNSUtilities(MAASServerTestCase):60class TestDNSUtilities(MAASServerTestCase):
@@ -70,34 +64,24 @@
70 self.patch(listener, "HANDLE_NOTIFY_DELAY", 0)64 self.patch(listener, "HANDLE_NOTIFY_DELAY", 0)
71 return listener65 return listener
7266
73 def test_zone_serial_parameters(self):67 def test_current_zone_serial_returns_serial_of_latest_publication(self):
74 self.assertThat(68 publication = DNSPublication(source=factory.make_name("source"))
75 zone_serial,69 publication.save()
76 MatchesStructure.byEquality(70 self.assertThat(
77 maxvalue=2 ** 32 - 1,71 int(current_zone_serial()),
78 minvalue=1,72 Equals(publication.serial))
79 increment=1,73
80 )74 def test_dns_force_reload_saves_new_publication(self):
81 )75 # A 'sys_dns' signal is also sent, but that is a side-effect of
8276 # inserting into the DNS publications table, and is tested as part of
83 def test_current_zone_serial_returns_same_serial(self):77 # the system triggers code.
84 zone_serial.create_if_not_exists()78 self.assertRaises(
85 initial = current_zone_serial()79 DNSPublication.DoesNotExist,
86 self.assertEquals(initial, current_zone_serial())80 DNSPublication.objects.get_most_recent)
8781 dns_force_reload()
88 @wait_for(30)82 self.assertThat(
89 @inlineCallbacks83 DNSPublication.objects.get_most_recent(),
90 def test_dns_force_reload_sends_notification(self):84 MatchesStructure.byEquality(source="Force reload"))
91 dv = DeferredValue()
92 listener = self.make_listener_without_delay()
93 listener.register(
94 "sys_dns", lambda *args: dv.set(args))
95 yield listener.startService()
96 try:
97 yield deferToDatabase(transactional(dns_force_reload))
98 yield dv.get(timeout=2)
99 finally:
100 yield listener.stopService()
10185
10286
103class TestDNSServer(MAASServerTestCase):87class TestDNSServer(MAASServerTestCase):
@@ -112,8 +96,9 @@
11296
113 def setUp(self):97 def setUp(self):
114 super(TestDNSServer, self).setUp()98 super(TestDNSServer, self).setUp()
115 # Make sure the zone_serial is created.99 # Ensure there's an initial DNS publication. Outside of tests this is
116 zone_serial.create_if_not_exists()100 # guaranteed by a migration.
101 DNSPublication(source="Initial").save()
117 # Allow test-local changes to configuration.102 # Allow test-local changes to configuration.
118 self.useFixture(RegionConfigurationFixture())103 self.useFixture(RegionConfigurationFixture())
119 # Immediately make DNS changes as they're needed.104 # Immediately make DNS changes as they're needed.
@@ -202,7 +187,7 @@
202 else:187 else:
203 serial = ans.rrset.items[0].serial188 serial = ans.rrset.items[0].serial
204189
205 if serial == zone_serial.current():190 if serial == DNSPublication.objects.get_most_recent().serial:
206 # The zone is up-to-date; we're done.191 # The zone is up-to-date; we're done.
207 return192 return
208 else:193 else:
209194
=== added file 'src/maasserver/migrations/builtin/maasserver/0057_initial_dns_publication.py'
--- src/maasserver/migrations/builtin/maasserver/0057_initial_dns_publication.py 1970-01-01 00:00:00 +0000
+++ src/maasserver/migrations/builtin/maasserver/0057_initial_dns_publication.py 2016-04-29 11:37:21 +0000
@@ -0,0 +1,37 @@
1# -*- coding: utf-8 -*-
2from django.db import migrations
3
4
5source = "Initial publication"
6
7
8def create_initial_publication(apps, schema_editor):
9 # We can't import the DNSPublication model directly as it may be a newer
10 # version than this migration expects. We use the historical version.
11 DNSPublication = apps.get_model("maasserver", "DNSPublication")
12 if not DNSPublication.objects.all().exists():
13 publication = DNSPublication(source=source)
14 publication.save()
15
16
17def remove_initial_publication(apps, schema_editor):
18 # We can't import the DNSPublication model directly as it may be a newer
19 # version than this migration expects. We use the historical version.
20 DNSPublication = apps.get_model("maasserver", "DNSPublication")
21 for publication in DNSPublication.objects.order_by("id")[:1]:
22 if publication.source == source:
23 publication.delete()
24
25
26class Migration(migrations.Migration):
27
28 dependencies = [
29 ('maasserver', '0056_zone_serial_ownership'),
30 ]
31
32 operations = [
33 migrations.RunPython(
34 create_initial_publication,
35 remove_initial_publication,
36 ),
37 ]
038
=== added file 'src/maasserver/migrations/builtin/maasserver/0058_bigger_integer_for_dns_publication_serial.py'
--- src/maasserver/migrations/builtin/maasserver/0058_bigger_integer_for_dns_publication_serial.py 1970-01-01 00:00:00 +0000
+++ src/maasserver/migrations/builtin/maasserver/0058_bigger_integer_for_dns_publication_serial.py 2016-04-29 11:37:21 +0000
@@ -0,0 +1,22 @@
1# -*- coding: utf-8 -*-
2import django.core.validators
3from django.db import (
4 migrations,
5 models,
6)
7import maasserver.models.dnspublication
8
9
10class Migration(migrations.Migration):
11
12 dependencies = [
13 ('maasserver', '0057_initial_dns_publication'),
14 ]
15
16 operations = [
17 migrations.AlterField(
18 model_name='dnspublication',
19 name='serial',
20 field=models.BigIntegerField(editable=False, validators=(django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(4294967295)), default=maasserver.models.dnspublication.next_serial),
21 ),
22 ]
023
=== added file 'src/maasserver/migrations/builtin/maasserver/0059_merge.py'
--- src/maasserver/migrations/builtin/maasserver/0059_merge.py 1970-01-01 00:00:00 +0000
+++ src/maasserver/migrations/builtin/maasserver/0059_merge.py 2016-04-29 11:37:21 +0000
@@ -0,0 +1,13 @@
1# -*- coding: utf-8 -*-
2from django.db import migrations
3
4
5class Migration(migrations.Migration):
6
7 dependencies = [
8 ('maasserver', '0058_bigger_integer_for_dns_publication_serial'),
9 ('maasserver', '0057_merge'),
10 ]
11
12 operations = [
13 ]
014
=== modified file 'src/maasserver/models/dnspublication.py'
--- src/maasserver/models/dnspublication.py 2016-04-29 11:37:21 +0000
+++ src/maasserver/models/dnspublication.py 2016-04-29 11:37:21 +0000
@@ -16,9 +16,9 @@
16 Model,16 Model,
17)17)
18from django.db.models.fields import (18from django.db.models.fields import (
19 BigIntegerField,
19 CharField,20 CharField,
20 DateTimeField,21 DateTimeField,
21 IntegerField,
22)22)
23from maasserver import DefaultMeta23from maasserver import DefaultMeta
24from maasserver.sequence import (24from maasserver.sequence import (
@@ -42,12 +42,26 @@
42 """Manager for DNS publishing records."""42 """Manager for DNS publishing records."""
4343
44 def get_most_recent(self):44 def get_most_recent(self):
45 """Return the most recently inserted `DNSPublication`."""45 """Return the most recently inserted `DNSPublication`.
46 return self.order_by("-id")[0]46
47 :raise DoesNotExist: If DNS has never been published.
48 """
49 for publication in self.order_by("-id")[:1]:
50 return publication
51 else:
52 # This is unlikely to be a problem in a running MAAS installation,
53 # but it can crop up a lot in tests because we can't, for example,
54 # use migrations to provide an initial publication.
55 raise self.model.DoesNotExist() from None
4756
48 def collect_garbage(self):57 def collect_garbage(self):
49 """Delete all but the most recently inserted `DNSPublication`."""58 """Delete all but the most recently inserted `DNSPublication`."""
50 self.filter(id__lt=self.get_most_recent().id).delete()59 try:
60 publication = self.get_most_recent()
61 except self.model.DoesNotExist:
62 pass # Nothing to do.
63 else:
64 self.filter(id__lt=publication.id).delete()
5165
5266
53class DNSPublication(Model):67class DNSPublication(Model):
@@ -67,7 +81,7 @@
6781
68 # The serial number with which to publish the zone. We don't use the82 # The serial number with which to publish the zone. We don't use the
69 # primary key for this because zone serials are allowed to cycle.83 # primary key for this because zone serials are allowed to cycle.
70 serial = IntegerField(84 serial = BigIntegerField(
71 editable=False, null=False, default=next_serial, unique=False,85 editable=False, null=False, default=next_serial, unique=False,
72 validators=(86 validators=(
73 MinValueValidator(zone_serial.minvalue),87 MinValueValidator(zone_serial.minvalue),
7488
=== modified file 'src/maasserver/models/tests/test_dnspublication.py'
--- src/maasserver/models/tests/test_dnspublication.py 2016-04-21 16:32:47 +0000
+++ src/maasserver/models/tests/test_dnspublication.py 2016-04-29 11:37:21 +0000
@@ -11,7 +11,11 @@
11)11)
12from random import randint12from random import randint
1313
14from maasserver.models.dnspublication import DNSPublication14from django.db import connection
15from maasserver.models.dnspublication import (
16 DNSPublication,
17 zone_serial,
18)
15from maasserver.testing.factory import factory19from maasserver.testing.factory import factory
16from maasserver.testing.testcase import MAASServerTestCase20from maasserver.testing.testcase import MAASServerTestCase
17from testtools.matchers import (21from testtools.matchers import (
@@ -24,6 +28,29 @@
24)28)
2529
2630
31class TestZoneSerial(MAASServerTestCase):
32 """Tests for the `maasserver_zone_serial_seq` sequence."""
33
34 def test_parameters(self):
35 self.assertThat(
36 zone_serial, MatchesStructure.byEquality(
37 maxvalue=2 ** 32 - 1, minvalue=1, increment=1, cycle=True,
38 owner="maasserver_dnspublication.serial",
39 ))
40
41 def test_parameters_in_database(self):
42 zone_serial.create_if_not_exists()
43 query = (
44 "SELECT start_value, increment_by, max_value, "
45 "min_value, is_cycled FROM %s" % zone_serial.name
46 )
47 with connection.cursor() as cursor:
48 cursor.execute(query)
49 self.assertThat(
50 cursor.fetchone(), Equals(
51 (1, 1, 2 ** 32 - 1, 1, True)))
52
53
27class TestDNSPublication(MAASServerTestCase):54class TestDNSPublication(MAASServerTestCase):
28 """Test the `DNSPublication` model."""55 """Test the `DNSPublication` model."""
2956
@@ -68,10 +95,14 @@
68 MatchesStructure(serial=Equals(10)))95 MatchesStructure(serial=Equals(10)))
6996
70 def test_get_most_recent_crashes_when_no_publications(self):97 def test_get_most_recent_crashes_when_no_publications(self):
71 # This is okay because we're going to ensure (using a migration) that98 # This is okay because we ensure (using a migration) that there is
72 # there is never less than one publication in the table. If this crash99 # never less than one publication in the table. If this crash happens
73 # happens we have bigger problems.100 # we have bigger problems. However, we do not currently use migrations
74 self.assertRaises(IndexError, DNSPublication.objects.get_most_recent)101 # in tests, so it is important to have a deterministic outcome when
102 # there are no publications.
103 self.assertRaises(
104 DNSPublication.DoesNotExist,
105 DNSPublication.objects.get_most_recent)
75106
76 def test_collect_garbage_removes_all_but_most_recent_record(self):107 def test_collect_garbage_removes_all_but_most_recent_record(self):
77 for serial in range(10):108 for serial in range(10):
@@ -82,3 +113,8 @@
82 self.assertThat(113 self.assertThat(
83 DNSPublication.objects.get_most_recent(),114 DNSPublication.objects.get_most_recent(),
84 MatchesStructure(serial=Equals(serial)))115 MatchesStructure(serial=Equals(serial)))
116
117 def test_collect_garbage_does_nothing_when_no_publications(self):
118 self.assertThat(DNSPublication.objects.all(), HasLength(0))
119 DNSPublication.objects.collect_garbage()
120 self.assertThat(DNSPublication.objects.all(), HasLength(0))
85121
=== modified file 'src/maasserver/triggers/system.py'
--- src/maasserver/triggers/system.py 2016-04-29 11:37:21 +0000
+++ src/maasserver/triggers/system.py 2016-04-29 11:37:21 +0000
@@ -787,11 +787,26 @@
787DNS_SUBNET_UPDATE = dedent("""\787DNS_SUBNET_UPDATE = dedent("""\
788 CREATE OR REPLACE FUNCTION sys_dns_subnet_update()788 CREATE OR REPLACE FUNCTION sys_dns_subnet_update()
789 RETURNS trigger as $$789 RETURNS trigger as $$
790 DECLARE
791 changes text[];
790 BEGIN792 BEGIN
791 IF OLD.cidr != NEW.cidr OR OLD.rdns_mode != NEW.rdns_mode THEN793 IF OLD.cidr != NEW.cidr THEN
792 -- Increment the zone serial.794 changes := changes || (
793 PERFORM nextval('maasserver_zone_serial_seq');795 'CIDR changed from ' || OLD.cidr || ' to ' || NEW.cidr);
794 PERFORM pg_notify('sys_dns', '');796 END IF;
797 IF OLD.rdns_mode != NEW.rdns_mode THEN
798 changes := changes || (
799 'RDNS mode changed from ' || OLD.rdns_mode || ' to ' ||
800 NEW.rdns_mode);
801 END IF;
802 IF array_length(changes, 1) != 0 THEN
803 INSERT INTO maasserver_dnspublication
804 (serial, created, source)
805 VALUES
806 (nextval('maasserver_zone_serial_seq'), now(),
807 substring(
808 ('Subnet ' || NEW.name || ': ' || array_to_string(changes, ', '))
809 FOR 255));
795 END IF;810 END IF;
796 RETURN NEW;811 RETURN NEW;
797 END;812 END;
@@ -804,11 +819,25 @@
804DNS_NODE_UPDATE = dedent("""\819DNS_NODE_UPDATE = dedent("""\
805 CREATE OR REPLACE FUNCTION sys_dns_node_update()820 CREATE OR REPLACE FUNCTION sys_dns_node_update()
806 RETURNS trigger as $$821 RETURNS trigger as $$
822 DECLARE
823 changes text[];
807 BEGIN824 BEGIN
808 IF OLD.hostname != NEW.hostname OR OLD.domain_id != NEW.domain_id THEN825 IF OLD.hostname != NEW.hostname THEN
809 -- Increment the zone serial.826 changes := changes || (
810 PERFORM nextval('maasserver_zone_serial_seq');827 'hostname changed from ' || OLD.hostname || ' to ' || NEW.hostname);
811 PERFORM pg_notify('sys_dns', '');828 END IF;
829 IF OLD.domain_id != NEW.domain_id THEN
830 changes := changes || 'domain changed'::text;
831 END IF;
832 IF array_length(changes, 1) != 0 THEN
833 INSERT INTO maasserver_dnspublication
834 (serial, created, source)
835 VALUES
836 (nextval('maasserver_zone_serial_seq'), now(),
837 substring(
838 ('Node ' || NEW.system_id || ': ' ||
839 array_to_string(changes, ', '))
840 FOR 255));
812 END IF;841 END IF;
813 RETURN NEW;842 RETURN NEW;
814 END;843 END;
@@ -822,14 +851,29 @@
822DNS_INTERFACE_UPDATE = dedent("""\851DNS_INTERFACE_UPDATE = dedent("""\
823 CREATE OR REPLACE FUNCTION sys_dns_interface_update()852 CREATE OR REPLACE FUNCTION sys_dns_interface_update()
824 RETURNS trigger as $$853 RETURNS trigger as $$
854 DECLARE
855 changes text[];
825 BEGIN856 BEGIN
826 IF (OLD.name != NEW.name OR857 IF OLD.name != NEW.name THEN
827 (OLD.node_id IS NULL AND NEW.node_id IS NOT NULL) OR858 changes := changes || (
828 (OLD.node_id IS NOT NULL AND NEW.node_id IS NULL) OR859 'renamed from ' || OLD.name || ' to ' || NEW.name);
829 (OLD.node_id != NEW.node_id)) THEN860 END IF;
830 -- Increment the zone serial.861 IF OLD.node_id IS NULL AND NEW.node_id IS NOT NULL THEN
831 PERFORM nextval('maasserver_zone_serial_seq');862 changes := changes || 'node set'::text;
832 PERFORM pg_notify('sys_dns', '');863 ELSIF OLD.node_id IS NOT NULL AND NEW.node_id IS NULL THEN
864 changes := changes || 'node unset'::text;
865 ELSIF OLD.node_id != NEW.node_id THEN
866 changes := changes || 'node changed'::text;
867 END IF;
868 IF array_length(changes, 1) != 0 THEN
869 INSERT INTO maasserver_dnspublication
870 (serial, created, source)
871 VALUES
872 (nextval('maasserver_zone_serial_seq'), now(),
873 substring(
874 ('Interface ' || NEW.name || ': ' ||
875 array_to_string(changes, ', '))
876 FOR 255));
833 END IF;877 END IF;
834 RETURN NEW;878 RETURN NEW;
835 END;879 END;
@@ -847,10 +891,14 @@
847 -- Only care about the891 -- Only care about the
848 IF (NEW.name = 'upstream_dns' OR892 IF (NEW.name = 'upstream_dns' OR
849 NEW.name = 'default_dns_ttl' OR893 NEW.name = 'default_dns_ttl' OR
850 NEW.name = 'windows_kms_host') THEN894 NEW.name = 'windows_kms_host')
851 -- Increment the zone serial.895 THEN
852 PERFORM nextval('maasserver_zone_serial_seq');896 INSERT INTO maasserver_dnspublication
853 PERFORM pg_notify('sys_dns', '');897 (serial, created, source)
898 VALUES
899 (nextval('maasserver_zone_serial_seq'), now(), substring(
900 ('Configuration ' || NEW.name || ' set to ' || NEW.value)
901 FOR 255));
854 END IF;902 END IF;
855 RETURN NEW;903 RETURN NEW;
856 END;904 END;
@@ -869,16 +917,33 @@
869 IF (OLD.value != NEW.value AND (917 IF (OLD.value != NEW.value AND (
870 NEW.name = 'upstream_dns' OR918 NEW.name = 'upstream_dns' OR
871 NEW.name = 'default_dns_ttl' OR919 NEW.name = 'default_dns_ttl' OR
872 NEW.name = 'windows_kms_host')) THEN920 NEW.name = 'windows_kms_host'))
873 -- Increment the zone serial.921 THEN
874 PERFORM nextval('maasserver_zone_serial_seq');922 INSERT INTO maasserver_dnspublication
875 PERFORM pg_notify('sys_dns', '');923 (serial, created, source)
924 VALUES
925 (nextval('maasserver_zone_serial_seq'), now(), substring(
926 ('Configuration ' || NEW.name || ' changed from ' ||
927 OLD.value || ' to ' || NEW.value)
928 FOR 255));
876 END IF;929 END IF;
877 RETURN NEW;930 RETURN NEW;
878 END;931 END;
879 $$ LANGUAGE plpgsql;932 $$ LANGUAGE plpgsql;
880 """)933 """)
881934
935# Triggered when DNS needs to be published. In essense this means on insert
936# into maasserver_dnspublication.
937DNS_PUBLISH = dedent("""\
938 CREATE OR REPLACE FUNCTION sys_dns_publish()
939 RETURNS trigger AS $$
940 BEGIN
941 PERFORM pg_notify('sys_dns', '');
942 RETURN NEW;
943 END;
944 $$ LANGUAGE plpgsql;
945 """)
946
882947
883# Triggered when a subnet is updated. Increments notifies that proxy needs to948# Triggered when a subnet is updated. Increments notifies that proxy needs to
884# be updated. Only watches changes on the cidr and allow_proxy.949# be updated. Only watches changes on the cidr and allow_proxy.
@@ -896,22 +961,23 @@
896961
897962
898def render_sys_dns_procedure(proc_name, on_delete=False):963def render_sys_dns_procedure(proc_name, on_delete=False):
899 """Render a database procedure with name `proc_name` that increments964 """Render a database procedure that creates a new DNS publication.
900 the zone serial and notifies that a DNS update is needed.
901965
902 :param proc_name: Name of the procedure.966 :param proc_name: Name of the procedure.
903 :param on_delete: True when procedure will be used as a delete trigger.967 :param on_delete: True when procedure will be used as a delete trigger.
904 """968 """
905 return dedent("""\969 return dedent("""\
906 CREATE OR REPLACE FUNCTION %s() RETURNS trigger AS $$970 CREATE OR REPLACE FUNCTION {proc}() RETURNS trigger AS $$
907 BEGIN971 BEGIN
908 -- Increment the zone serial.972 INSERT INTO maasserver_dnspublication
909 PERFORM nextval('maasserver_zone_serial_seq');973 (serial, created, source)
910 PERFORM pg_notify('sys_dns', '');974 VALUES
911 RETURN %s;975 (nextval('maasserver_zone_serial_seq'), now(),
976 substring('Call to {proc}' FOR 255));
977 RETURN {rval};
912 END;978 END;
913 $$ LANGUAGE plpgsql;979 $$ LANGUAGE plpgsql;
914 """ % (proc_name, 'NEW' if not on_delete else 'OLD'))980 """).format(proc=proc_name, rval='OLD' if on_delete else 'NEW')
915981
916982
917def render_sys_proxy_procedure(proc_name, on_delete=False):983def render_sys_proxy_procedure(proc_name, on_delete=False):
@@ -1107,6 +1173,12 @@
1107 "maasserver_dnsdata",1173 "maasserver_dnsdata",
1108 "sys_dns_dnsdata_delete", "delete")1174 "sys_dns_dnsdata_delete", "delete")
11091175
1176 # - DNSPublication
1177 register_procedure(DNS_PUBLISH)
1178 register_trigger(
1179 "maasserver_dnspublication",
1180 "sys_dns_publish", "insert")
1181
1110 # - Subnet1182 # - Subnet
1111 register_procedure(1183 register_procedure(
1112 render_sys_dns_procedure("sys_dns_subnet_insert"))1184 render_sys_dns_procedure("sys_dns_subnet_insert"))
11131185
=== renamed file 'src/maasserver/triggers/tests/helper.py' => 'src/maasserver/triggers/testing.py'
--- src/maasserver/triggers/tests/helper.py 2016-04-29 11:37:21 +0000
+++ src/maasserver/triggers/testing.py 2016-04-29 11:37:21 +0000
@@ -17,7 +17,7 @@
17from maasserver.models.cacheset import CacheSet17from maasserver.models.cacheset import CacheSet
18from maasserver.models.dhcpsnippet import DHCPSnippet18from maasserver.models.dhcpsnippet import DHCPSnippet
19from maasserver.models.dnsdata import DNSData19from maasserver.models.dnsdata import DNSData
20from maasserver.models.dnspublication import zone_serial20from maasserver.models.dnspublication import DNSPublication
21from maasserver.models.dnsresource import DNSResource21from maasserver.models.dnsresource import DNSResource
22from maasserver.models.domain import Domain22from maasserver.models.domain import Domain
23from maasserver.models.event import Event23from maasserver.models.event import Event
@@ -55,7 +55,15 @@
55)55)
56from maasserver.utils.threads import deferToDatabase56from maasserver.utils.threads import deferToDatabase
57from metadataserver.models import NodeResult57from metadataserver.models import NodeResult
58from twisted.internet.defer import inlineCallbacks58from testtools.matchers import (
59 GreaterThan,
60 Is,
61 Not,
62)
63from twisted.internet.defer import (
64 inlineCallbacks,
65 returnValue,
66)
5967
6068
61wait_for_reactor = wait_for(30) # 30 seconds.69wait_for_reactor = wait_for(30) # 30 seconds.
@@ -638,12 +646,42 @@
638class DNSHelpersMixin:646class DNSHelpersMixin:
639 """Helper to get the zone serial and to assert it was incremented."""647 """Helper to get the zone serial and to assert it was incremented."""
640648
641 def get_zone_serial_current(self):649 @transactional
642 return deferToDatabase(transactional(zone_serial.current))650 def getPublication(self):
643651 try:
644 @inlineCallbacks652 return DNSPublication.objects.get_most_recent()
645 def assertZoneSerialIncrement(self, previous):653 except DNSPublication.DoesNotExist:
646 current = yield deferToDatabase(transactional(zone_serial.current))654 return None
647 self.assertTrue(655
648 current > previous,656 @inlineCallbacks
649 "Zone serial was not incremented.")657 def capturePublication(self):
658 """Capture the most recent `DNSPublication` record."""
659 self.__publication = yield deferToDatabase(self.getPublication)
660 returnValue(self.__publication)
661
662 def getCapturedPublication(self):
663 """Return the captured publication."""
664 try:
665 return self.__publication
666 except AttributeError:
667 self.fail(
668 "No reference publication has been captured; "
669 "use `capturePublication` before calling "
670 "`getCapturedPublication`.")
671
672 @inlineCallbacks
673 def assertPublicationUpdated(self):
674 """Assert there's a newer `DNSPublication` record.
675
676 Call `capturePublication` first to obtain a reference record.
677 """
678 old = self.getCapturedPublication()
679 new = yield self.capturePublication()
680 if old is None:
681 self.assertThat(
682 new, Not(Is(None)),
683 "DNS has not been published at all.")
684 else:
685 self.assertThat(
686 new.serial, GreaterThan(old.serial),
687 "DNS has not been published again.")
650688
=== modified file 'src/maasserver/triggers/tests/test_system_listener.py'
--- src/maasserver/triggers/tests/test_system_listener.py 2016-03-31 23:34:55 +0000
+++ src/maasserver/triggers/tests/test_system_listener.py 2016-04-29 11:37:21 +0000
@@ -10,6 +10,7 @@
10 datetime,10 datetime,
11 timedelta,11 timedelta,
12)12)
13import json
13import random14import random
1415
15from crochet import wait_for16from crochet import wait_for
@@ -29,7 +30,7 @@
29from maasserver.testing.factory import factory30from maasserver.testing.factory import factory
30from maasserver.testing.testcase import MAASTransactionServerTestCase31from maasserver.testing.testcase import MAASTransactionServerTestCase
31from maasserver.triggers.system import register_system_triggers32from maasserver.triggers.system import register_system_triggers
32from maasserver.triggers.tests.helper import (33from maasserver.triggers.testing import (
33 DNSHelpersMixin,34 DNSHelpersMixin,
34 TransactionalHelpersMixin,35 TransactionalHelpersMixin,
35)36)
@@ -37,6 +38,7 @@
37from maasserver.utils.threads import deferToDatabase38from maasserver.utils.threads import deferToDatabase
38from netaddr import IPAddress39from netaddr import IPAddress
39from provisioningserver.utils.twisted import DeferredValue40from provisioningserver.utils.twisted import DeferredValue
41from testtools.matchers import Equals
40from twisted.internet.defer import (42from twisted.internet.defer import (
41 CancelledError,43 CancelledError,
42 inlineCallbacks,44 inlineCallbacks,
@@ -2293,7 +2295,7 @@
2293 @inlineCallbacks2295 @inlineCallbacks
2294 def test_sends_message_for_domain_insert(self):2296 def test_sends_message_for_domain_insert(self):
2295 yield deferToDatabase(register_system_triggers)2297 yield deferToDatabase(register_system_triggers)
2296 zone_serial = yield self.get_zone_serial_current()2298 yield self.capturePublication()
2297 dv = DeferredValue()2299 dv = DeferredValue()
2298 listener = self.make_listener_without_delay()2300 listener = self.make_listener_without_delay()
2299 listener.register(2301 listener.register(
@@ -2302,16 +2304,19 @@
2302 try:2304 try:
2303 yield deferToDatabase(self.create_domain)2305 yield deferToDatabase(self.create_domain)
2304 yield dv.get(timeout=2)2306 yield dv.get(timeout=2)
2305 yield self.assertZoneSerialIncrement(zone_serial)2307 yield self.assertPublicationUpdated()
2306 finally:2308 finally:
2307 yield listener.stopService()2309 yield listener.stopService()
2310 self.assertThat(
2311 self.getCapturedPublication().source,
2312 Equals("Call to sys_dns_domain_insert"))
23082313
2309 @wait_for_reactor2314 @wait_for_reactor
2310 @inlineCallbacks2315 @inlineCallbacks
2311 def test_sends_message_for_domain_update(self):2316 def test_sends_message_for_domain_update(self):
2312 yield deferToDatabase(register_system_triggers)2317 yield deferToDatabase(register_system_triggers)
2313 domain = yield deferToDatabase(self.create_domain)2318 domain = yield deferToDatabase(self.create_domain)
2314 zone_serial = yield self.get_zone_serial_current()2319 yield self.capturePublication()
2315 dv = DeferredValue()2320 dv = DeferredValue()
2316 listener = self.make_listener_without_delay()2321 listener = self.make_listener_without_delay()
2317 listener.register(2322 listener.register(
@@ -2322,16 +2327,19 @@
2322 "name": factory.make_name("domain"),2327 "name": factory.make_name("domain"),
2323 })2328 })
2324 yield dv.get(timeout=2)2329 yield dv.get(timeout=2)
2325 yield self.assertZoneSerialIncrement(zone_serial)2330 yield self.assertPublicationUpdated()
2326 finally:2331 finally:
2327 yield listener.stopService()2332 yield listener.stopService()
2333 self.assertThat(
2334 self.getCapturedPublication().source,
2335 Equals("Call to sys_dns_domain_update"))
23282336
2329 @wait_for_reactor2337 @wait_for_reactor
2330 @inlineCallbacks2338 @inlineCallbacks
2331 def test_sends_message_for_domain_delete(self):2339 def test_sends_message_for_domain_delete(self):
2332 yield deferToDatabase(register_system_triggers)2340 yield deferToDatabase(register_system_triggers)
2333 domain = yield deferToDatabase(self.create_domain)2341 domain = yield deferToDatabase(self.create_domain)
2334 zone_serial = yield self.get_zone_serial_current()2342 yield self.capturePublication()
2335 dv = DeferredValue()2343 dv = DeferredValue()
2336 listener = self.make_listener_without_delay()2344 listener = self.make_listener_without_delay()
2337 listener.register(2345 listener.register(
@@ -2340,9 +2348,12 @@
2340 try:2348 try:
2341 yield deferToDatabase(self.delete_domain, domain.id)2349 yield deferToDatabase(self.delete_domain, domain.id)
2342 yield dv.get(timeout=2)2350 yield dv.get(timeout=2)
2343 yield self.assertZoneSerialIncrement(zone_serial)2351 yield self.assertPublicationUpdated()
2344 finally:2352 finally:
2345 yield listener.stopService()2353 yield listener.stopService()
2354 self.assertThat(
2355 self.getCapturedPublication().source,
2356 Equals("Call to sys_dns_domain_delete"))
23462357
23472358
2348class TestDNSStaticIPAddressListener(2359class TestDNSStaticIPAddressListener(
@@ -2355,7 +2366,7 @@
2355 def test_sends_message_for_staticipaddress_update(self):2366 def test_sends_message_for_staticipaddress_update(self):
2356 yield deferToDatabase(register_system_triggers)2367 yield deferToDatabase(register_system_triggers)
2357 sip = yield deferToDatabase(self.create_staticipaddress)2368 sip = yield deferToDatabase(self.create_staticipaddress)
2358 zone_serial = yield self.get_zone_serial_current()2369 yield self.capturePublication()
2359 dv = DeferredValue()2370 dv = DeferredValue()
2360 listener = self.make_listener_without_delay()2371 listener = self.make_listener_without_delay()
2361 listener.register(2372 listener.register(
@@ -2366,9 +2377,12 @@
2366 "alloc_type": IPADDRESS_TYPE.STICKY,2377 "alloc_type": IPADDRESS_TYPE.STICKY,
2367 })2378 })
2368 yield dv.get(timeout=2)2379 yield dv.get(timeout=2)
2369 yield self.assertZoneSerialIncrement(zone_serial)2380 yield self.assertPublicationUpdated()
2370 finally:2381 finally:
2371 yield listener.stopService()2382 yield listener.stopService()
2383 self.assertThat(
2384 self.getCapturedPublication().source,
2385 Equals("Call to sys_dns_staticipaddress_update"))
23722386
23732387
2374class TestDNSInterfaceStaticIPAddressListener(2388class TestDNSInterfaceStaticIPAddressListener(
@@ -2381,7 +2395,7 @@
2381 def test_sends_message_for_interface_staticipaddress_link(self):2395 def test_sends_message_for_interface_staticipaddress_link(self):
2382 yield deferToDatabase(register_system_triggers)2396 yield deferToDatabase(register_system_triggers)
2383 interface = yield deferToDatabase(self.create_interface)2397 interface = yield deferToDatabase(self.create_interface)
2384 zone_serial = yield self.get_zone_serial_current()2398 yield self.capturePublication()
2385 dv = DeferredValue()2399 dv = DeferredValue()
2386 listener = self.make_listener_without_delay()2400 listener = self.make_listener_without_delay()
2387 listener.register(2401 listener.register(
@@ -2392,9 +2406,12 @@
2392 "interface": interface,2406 "interface": interface,
2393 })2407 })
2394 yield dv.get(timeout=2)2408 yield dv.get(timeout=2)
2395 yield self.assertZoneSerialIncrement(zone_serial)2409 yield self.assertPublicationUpdated()
2396 finally:2410 finally:
2397 yield listener.stopService()2411 yield listener.stopService()
2412 self.assertThat(
2413 self.getCapturedPublication().source,
2414 Equals("Call to sys_dns_nic_ip_link"))
23982415
2399 @wait_for_reactor2416 @wait_for_reactor
2400 @inlineCallbacks2417 @inlineCallbacks
@@ -2404,7 +2421,7 @@
2404 sip = yield deferToDatabase(self.create_staticipaddress, {2421 sip = yield deferToDatabase(self.create_staticipaddress, {
2405 "interface": interface,2422 "interface": interface,
2406 })2423 })
2407 zone_serial = yield self.get_zone_serial_current()2424 yield self.capturePublication()
2408 dv = DeferredValue()2425 dv = DeferredValue()
2409 listener = self.make_listener_without_delay()2426 listener = self.make_listener_without_delay()
2410 listener.register(2427 listener.register(
@@ -2413,9 +2430,12 @@
2413 try:2430 try:
2414 yield deferToDatabase(self.delete_staticipaddress, sip.id)2431 yield deferToDatabase(self.delete_staticipaddress, sip.id)
2415 yield dv.get(timeout=2)2432 yield dv.get(timeout=2)
2416 yield self.assertZoneSerialIncrement(zone_serial)2433 yield self.assertPublicationUpdated()
2417 finally:2434 finally:
2418 yield listener.stopService()2435 yield listener.stopService()
2436 self.assertThat(
2437 self.getCapturedPublication().source,
2438 Equals("Call to sys_dns_nic_ip_unlink"))
24192439
24202440
2421class TestDNSDNSResourceListener(2441class TestDNSDNSResourceListener(
@@ -2427,25 +2447,31 @@
2427 @inlineCallbacks2447 @inlineCallbacks
2428 def test_sends_message_for_dnsresource_insert(self):2448 def test_sends_message_for_dnsresource_insert(self):
2429 yield deferToDatabase(register_system_triggers)2449 yield deferToDatabase(register_system_triggers)
2430 zone_serial = yield self.get_zone_serial_current()2450 yield self.capturePublication()
2431 dv = DeferredValue()2451 dv = DeferredValue()
2432 listener = self.make_listener_without_delay()2452 listener = self.make_listener_without_delay()
2433 listener.register(2453 listener.register(
2434 "sys_dns", lambda *args: dv.set(args))2454 "sys_dns", lambda *args: dv.set(args))
2435 yield listener.startService()2455 yield listener.startService()
2436 try:2456 try:
2437 yield deferToDatabase(self.create_dnsresource)2457 # Pass ip_addresses=[] to avoid an extra .save() -- and thus an
2458 # UPDATE -- in the factory method.
2459 yield deferToDatabase(
2460 self.create_dnsresource, {"ip_addresses": []})
2438 yield dv.get(timeout=2)2461 yield dv.get(timeout=2)
2439 yield self.assertZoneSerialIncrement(zone_serial)2462 yield self.assertPublicationUpdated()
2440 finally:2463 finally:
2441 yield listener.stopService()2464 yield listener.stopService()
2465 self.assertThat(
2466 self.getCapturedPublication().source,
2467 Equals("Call to sys_dns_dnsresource_insert"))
24422468
2443 @wait_for_reactor2469 @wait_for_reactor
2444 @inlineCallbacks2470 @inlineCallbacks
2445 def test_sends_message_for_dnsresource_update(self):2471 def test_sends_message_for_dnsresource_update(self):
2446 yield deferToDatabase(register_system_triggers)2472 yield deferToDatabase(register_system_triggers)
2447 resource = yield deferToDatabase(self.create_dnsresource)2473 resource = yield deferToDatabase(self.create_dnsresource)
2448 zone_serial = yield self.get_zone_serial_current()2474 yield self.capturePublication()
2449 dv = DeferredValue()2475 dv = DeferredValue()
2450 listener = self.make_listener_without_delay()2476 listener = self.make_listener_without_delay()
2451 listener.register(2477 listener.register(
@@ -2456,16 +2482,19 @@
2456 "name": factory.make_name("resource"),2482 "name": factory.make_name("resource"),
2457 })2483 })
2458 yield dv.get(timeout=2)2484 yield dv.get(timeout=2)
2459 yield self.assertZoneSerialIncrement(zone_serial)2485 yield self.assertPublicationUpdated()
2460 finally:2486 finally:
2461 yield listener.stopService()2487 yield listener.stopService()
2488 self.assertThat(
2489 self.getCapturedPublication().source,
2490 Equals("Call to sys_dns_dnsresource_update"))
24622491
2463 @wait_for_reactor2492 @wait_for_reactor
2464 @inlineCallbacks2493 @inlineCallbacks
2465 def test_sends_message_for_dnsresource_delete(self):2494 def test_sends_message_for_dnsresource_delete(self):
2466 yield deferToDatabase(register_system_triggers)2495 yield deferToDatabase(register_system_triggers)
2467 resource = yield deferToDatabase(self.create_dnsresource)2496 resource = yield deferToDatabase(self.create_dnsresource)
2468 zone_serial = yield self.get_zone_serial_current()2497 yield self.capturePublication()
2469 dv = DeferredValue()2498 dv = DeferredValue()
2470 listener = self.make_listener_without_delay()2499 listener = self.make_listener_without_delay()
2471 listener.register(2500 listener.register(
@@ -2474,9 +2503,12 @@
2474 try:2503 try:
2475 yield deferToDatabase(self.delete_dnsresource, resource.id)2504 yield deferToDatabase(self.delete_dnsresource, resource.id)
2476 yield dv.get(timeout=2)2505 yield dv.get(timeout=2)
2477 yield self.assertZoneSerialIncrement(zone_serial)2506 yield self.assertPublicationUpdated()
2478 finally:2507 finally:
2479 yield listener.stopService()2508 yield listener.stopService()
2509 self.assertThat(
2510 self.getCapturedPublication().source,
2511 Equals("Call to sys_dns_dnsresource_delete"))
24802512
24812513
2482class TestDNSDNSResourceStaticIPAddressListener(2514class TestDNSDNSResourceStaticIPAddressListener(
@@ -2490,7 +2522,7 @@
2490 yield deferToDatabase(register_system_triggers)2522 yield deferToDatabase(register_system_triggers)
2491 resource = yield deferToDatabase(self.create_dnsresource)2523 resource = yield deferToDatabase(self.create_dnsresource)
2492 sip = yield deferToDatabase(self.create_staticipaddress)2524 sip = yield deferToDatabase(self.create_staticipaddress)
2493 zone_serial = yield self.get_zone_serial_current()2525 yield self.capturePublication()
2494 dv = DeferredValue()2526 dv = DeferredValue()
2495 listener = self.make_listener_without_delay()2527 listener = self.make_listener_without_delay()
2496 listener.register(2528 listener.register(
@@ -2499,9 +2531,12 @@
2499 try:2531 try:
2500 yield deferToDatabase(resource.ip_addresses.add, sip)2532 yield deferToDatabase(resource.ip_addresses.add, sip)
2501 yield dv.get(timeout=2)2533 yield dv.get(timeout=2)
2502 yield self.assertZoneSerialIncrement(zone_serial)2534 yield self.assertPublicationUpdated()
2503 finally:2535 finally:
2504 yield listener.stopService()2536 yield listener.stopService()
2537 self.assertThat(
2538 self.getCapturedPublication().source,
2539 Equals("Call to sys_dns_dnsresource_ip_link"))
25052540
2506 @wait_for_reactor2541 @wait_for_reactor
2507 @inlineCallbacks2542 @inlineCallbacks
@@ -2510,7 +2545,7 @@
2510 resource = yield deferToDatabase(self.create_dnsresource)2545 resource = yield deferToDatabase(self.create_dnsresource)
2511 sip = yield deferToDatabase(self.create_staticipaddress)2546 sip = yield deferToDatabase(self.create_staticipaddress)
2512 yield deferToDatabase(resource.ip_addresses.add, sip)2547 yield deferToDatabase(resource.ip_addresses.add, sip)
2513 zone_serial = yield self.get_zone_serial_current()2548 yield self.capturePublication()
2514 dv = DeferredValue()2549 dv = DeferredValue()
2515 listener = self.make_listener_without_delay()2550 listener = self.make_listener_without_delay()
2516 listener.register(2551 listener.register(
@@ -2519,9 +2554,12 @@
2519 try:2554 try:
2520 yield deferToDatabase(resource.ip_addresses.remove, sip)2555 yield deferToDatabase(resource.ip_addresses.remove, sip)
2521 yield dv.get(timeout=2)2556 yield dv.get(timeout=2)
2522 yield self.assertZoneSerialIncrement(zone_serial)2557 yield self.assertPublicationUpdated()
2523 finally:2558 finally:
2524 yield listener.stopService()2559 yield listener.stopService()
2560 self.assertThat(
2561 self.getCapturedPublication().source,
2562 Equals("Call to sys_dns_dnsresource_ip_unlink"))
25252563
25262564
2527class TestDNSDNSDataListener(2565class TestDNSDNSDataListener(
@@ -2533,7 +2571,7 @@
2533 @inlineCallbacks2571 @inlineCallbacks
2534 def test_sends_message_for_dnsdata_insert(self):2572 def test_sends_message_for_dnsdata_insert(self):
2535 yield deferToDatabase(register_system_triggers)2573 yield deferToDatabase(register_system_triggers)
2536 zone_serial = yield self.get_zone_serial_current()2574 yield self.capturePublication()
2537 dv = DeferredValue()2575 dv = DeferredValue()
2538 listener = self.make_listener_without_delay()2576 listener = self.make_listener_without_delay()
2539 listener.register(2577 listener.register(
@@ -2542,9 +2580,12 @@
2542 try:2580 try:
2543 yield deferToDatabase(self.create_dnsdata)2581 yield deferToDatabase(self.create_dnsdata)
2544 yield dv.get(timeout=2)2582 yield dv.get(timeout=2)
2545 yield self.assertZoneSerialIncrement(zone_serial)2583 yield self.assertPublicationUpdated()
2546 finally:2584 finally:
2547 yield listener.stopService()2585 yield listener.stopService()
2586 self.assertThat(
2587 self.getCapturedPublication().source,
2588 Equals("Call to sys_dns_dnsdata_insert"))
25482589
2549 @wait_for_reactor2590 @wait_for_reactor
2550 @inlineCallbacks2591 @inlineCallbacks
@@ -2554,7 +2595,7 @@
2554 "rrtype": "TXT",2595 "rrtype": "TXT",
2555 "rrdata": factory.make_name("txt"),2596 "rrdata": factory.make_name("txt"),
2556 })2597 })
2557 zone_serial = yield self.get_zone_serial_current()2598 yield self.capturePublication()
2558 dv = DeferredValue()2599 dv = DeferredValue()
2559 listener = self.make_listener_without_delay()2600 listener = self.make_listener_without_delay()
2560 listener.register(2601 listener.register(
@@ -2565,16 +2606,19 @@
2565 "rrdata": factory.make_name("txt"),2606 "rrdata": factory.make_name("txt"),
2566 })2607 })
2567 yield dv.get(timeout=2)2608 yield dv.get(timeout=2)
2568 yield self.assertZoneSerialIncrement(zone_serial)2609 yield self.assertPublicationUpdated()
2569 finally:2610 finally:
2570 yield listener.stopService()2611 yield listener.stopService()
2612 self.assertThat(
2613 self.getCapturedPublication().source,
2614 Equals("Call to sys_dns_dnsdata_update"))
25712615
2572 @wait_for_reactor2616 @wait_for_reactor
2573 @inlineCallbacks2617 @inlineCallbacks
2574 def test_sends_message_for_dnsdata_delete(self):2618 def test_sends_message_for_dnsdata_delete(self):
2575 yield deferToDatabase(register_system_triggers)2619 yield deferToDatabase(register_system_triggers)
2576 data = yield deferToDatabase(self.create_dnsdata)2620 data = yield deferToDatabase(self.create_dnsdata)
2577 zone_serial = yield self.get_zone_serial_current()2621 yield self.capturePublication()
2578 dv = DeferredValue()2622 dv = DeferredValue()
2579 listener = self.make_listener_without_delay()2623 listener = self.make_listener_without_delay()
2580 listener.register(2624 listener.register(
@@ -2583,9 +2627,12 @@
2583 try:2627 try:
2584 yield deferToDatabase(self.delete_dnsdata, data.id)2628 yield deferToDatabase(self.delete_dnsdata, data.id)
2585 yield dv.get(timeout=2)2629 yield dv.get(timeout=2)
2586 yield self.assertZoneSerialIncrement(zone_serial)2630 yield self.assertPublicationUpdated()
2587 finally:2631 finally:
2588 yield listener.stopService()2632 yield listener.stopService()
2633 self.assertThat(
2634 self.getCapturedPublication().source,
2635 Equals("Call to sys_dns_dnsdata_delete"))
25892636
25902637
2591class TestDNSSubnetListener(2638class TestDNSSubnetListener(
@@ -2597,7 +2644,7 @@
2597 @inlineCallbacks2644 @inlineCallbacks
2598 def test_sends_message_for_subnet_insert(self):2645 def test_sends_message_for_subnet_insert(self):
2599 yield deferToDatabase(register_system_triggers)2646 yield deferToDatabase(register_system_triggers)
2600 zone_serial = yield self.get_zone_serial_current()2647 yield self.capturePublication()
2601 dv = DeferredValue()2648 dv = DeferredValue()
2602 listener = self.make_listener_without_delay()2649 listener = self.make_listener_without_delay()
2603 listener.register(2650 listener.register(
@@ -2606,16 +2653,19 @@
2606 try:2653 try:
2607 yield deferToDatabase(self.create_subnet)2654 yield deferToDatabase(self.create_subnet)
2608 yield dv.get(timeout=2)2655 yield dv.get(timeout=2)
2609 yield self.assertZoneSerialIncrement(zone_serial)2656 yield self.assertPublicationUpdated()
2610 finally:2657 finally:
2611 yield listener.stopService()2658 yield listener.stopService()
2659 self.assertThat(
2660 self.getCapturedPublication().source,
2661 Equals("Call to sys_dns_subnet_insert"))
26122662
2613 @wait_for_reactor2663 @wait_for_reactor
2614 @inlineCallbacks2664 @inlineCallbacks
2615 def test_sends_message_for_subnet_cidr_update(self):2665 def test_sends_message_for_subnet_cidr_update(self):
2616 yield deferToDatabase(register_system_triggers)2666 yield deferToDatabase(register_system_triggers)
2617 subnet = yield deferToDatabase(self.create_subnet)2667 subnet = yield deferToDatabase(self.create_subnet)
2618 zone_serial = yield self.get_zone_serial_current()2668 yield self.capturePublication()
2619 dv = DeferredValue()2669 dv = DeferredValue()
2620 listener = self.make_listener_without_delay()2670 listener = self.make_listener_without_delay()
2621 listener.register(2671 listener.register(
@@ -2623,43 +2673,53 @@
2623 yield listener.startService()2673 yield listener.startService()
2624 try:2674 try:
2625 network = factory.make_ip4_or_6_network()2675 network = factory.make_ip4_or_6_network()
2676 cidr_old, cidr_new = subnet.cidr, str(network.cidr)
2626 yield deferToDatabase(self.update_subnet, subnet.id, {2677 yield deferToDatabase(self.update_subnet, subnet.id, {
2627 "cidr": str(network.cidr),2678 "cidr": cidr_new,
2628 "gateway_ip": factory.pick_ip_in_network(network),2679 "gateway_ip": factory.pick_ip_in_network(network),
2629 "dns_servers": [],2680 "dns_servers": [],
2630 })2681 })
2631 yield dv.get(timeout=2)2682 yield dv.get(timeout=2)
2632 yield self.assertZoneSerialIncrement(zone_serial)2683 yield self.assertPublicationUpdated()
2633 finally:2684 finally:
2634 yield listener.stopService()2685 yield listener.stopService()
2686 self.assertThat(
2687 self.getCapturedPublication().source, Equals(
2688 "Subnet %s: CIDR changed from %s to %s"
2689 % (subnet.name, cidr_old, cidr_new)))
26352690
2636 @wait_for_reactor2691 @wait_for_reactor
2637 @inlineCallbacks2692 @inlineCallbacks
2638 def test_sends_message_for_subnet_rdns_mode_update(self):2693 def test_sends_message_for_subnet_rdns_mode_update(self):
2639 yield deferToDatabase(register_system_triggers)2694 yield deferToDatabase(register_system_triggers)
2640 subnet = yield deferToDatabase(self.create_subnet)2695 subnet = yield deferToDatabase(self.create_subnet)
2641 zone_serial = yield self.get_zone_serial_current()2696 yield self.capturePublication()
2642 dv = DeferredValue()2697 dv = DeferredValue()
2643 listener = self.make_listener_without_delay()2698 listener = self.make_listener_without_delay()
2644 listener.register(2699 listener.register(
2645 "sys_dns", lambda *args: dv.set(args))2700 "sys_dns", lambda *args: dv.set(args))
2646 yield listener.startService()2701 yield listener.startService()
2647 try:2702 try:
2703 rdns_old = subnet.rdns_mode
2704 rdns_new = factory.pick_enum(RDNS_MODE, but_not=[rdns_old])
2648 yield deferToDatabase(self.update_subnet, subnet.id, {2705 yield deferToDatabase(self.update_subnet, subnet.id, {
2649 "rdns_mode": factory.pick_enum(2706 "rdns_mode": rdns_new,
2650 RDNS_MODE, but_not=[subnet.rdns_mode]),
2651 })2707 })
2652 yield dv.get(timeout=2)2708 yield dv.get(timeout=2)
2653 yield self.assertZoneSerialIncrement(zone_serial)2709 yield self.assertPublicationUpdated()
2654 finally:2710 finally:
2655 yield listener.stopService()2711 yield listener.stopService()
2712 self.assertThat(
2713 self.getCapturedPublication().source, Equals(
2714 "Subnet %s: RDNS mode changed from %s to %s"
2715 % (subnet.name, rdns_old, rdns_new)))
26562716
2657 @wait_for_reactor2717 @wait_for_reactor
2658 @inlineCallbacks2718 @inlineCallbacks
2659 def test_sends_message_for_subnet_delete(self):2719 def test_sends_message_for_subnet_delete(self):
2660 yield deferToDatabase(register_system_triggers)2720 yield deferToDatabase(register_system_triggers)
2661 subnet = yield deferToDatabase(self.create_subnet)2721 subnet = yield deferToDatabase(self.create_subnet)
2662 zone_serial = yield self.get_zone_serial_current()2722 yield self.capturePublication()
2663 dv = DeferredValue()2723 dv = DeferredValue()
2664 listener = self.make_listener_without_delay()2724 listener = self.make_listener_without_delay()
2665 listener.register(2725 listener.register(
@@ -2668,9 +2728,12 @@
2668 try:2728 try:
2669 yield deferToDatabase(self.delete_subnet, subnet.id)2729 yield deferToDatabase(self.delete_subnet, subnet.id)
2670 yield dv.get(timeout=2)2730 yield dv.get(timeout=2)
2671 yield self.assertZoneSerialIncrement(zone_serial)2731 yield self.assertPublicationUpdated()
2672 finally:2732 finally:
2673 yield listener.stopService()2733 yield listener.stopService()
2734 self.assertThat(
2735 self.getCapturedPublication().source,
2736 Equals("Call to sys_dns_subnet_delete"))
26742737
26752738
2676class TestDNSNodeListener(2739class TestDNSNodeListener(
@@ -2683,20 +2746,26 @@
2683 def test_sends_message_for_node_update_hostname(self):2746 def test_sends_message_for_node_update_hostname(self):
2684 yield deferToDatabase(register_system_triggers)2747 yield deferToDatabase(register_system_triggers)
2685 node = yield deferToDatabase(self.create_node)2748 node = yield deferToDatabase(self.create_node)
2686 zone_serial = yield self.get_zone_serial_current()2749 yield self.capturePublication()
2687 dv = DeferredValue()2750 dv = DeferredValue()
2688 listener = self.make_listener_without_delay()2751 listener = self.make_listener_without_delay()
2689 listener.register(2752 listener.register(
2690 "sys_dns", lambda *args: dv.set(args))2753 "sys_dns", lambda *args: dv.set(args))
2691 yield listener.startService()2754 yield listener.startService()
2692 try:2755 try:
2756 hostname_old = node.hostname
2757 hostname_new = factory.make_name("hostname")
2693 yield deferToDatabase(self.update_node, node.system_id, {2758 yield deferToDatabase(self.update_node, node.system_id, {
2694 "hostname": factory.make_name("hostname"),2759 "hostname": hostname_new,
2695 })2760 })
2696 yield dv.get(timeout=2)2761 yield dv.get(timeout=2)
2697 yield self.assertZoneSerialIncrement(zone_serial)2762 yield self.assertPublicationUpdated()
2698 finally:2763 finally:
2699 yield listener.stopService()2764 yield listener.stopService()
2765 self.assertThat(
2766 self.getCapturedPublication().source, Equals(
2767 "Node %s: hostname changed from %s to %s"
2768 % (node.system_id, hostname_old, hostname_new)))
27002769
2701 @wait_for_reactor2770 @wait_for_reactor
2702 @inlineCallbacks2771 @inlineCallbacks
@@ -2704,7 +2773,7 @@
2704 yield deferToDatabase(register_system_triggers)2773 yield deferToDatabase(register_system_triggers)
2705 node = yield deferToDatabase(self.create_node)2774 node = yield deferToDatabase(self.create_node)
2706 domain = yield deferToDatabase(self.create_domain)2775 domain = yield deferToDatabase(self.create_domain)
2707 zone_serial = yield self.get_zone_serial_current()2776 yield self.capturePublication()
2708 dv = DeferredValue()2777 dv = DeferredValue()
2709 listener = self.make_listener_without_delay()2778 listener = self.make_listener_without_delay()
2710 listener.register(2779 listener.register(
@@ -2715,16 +2784,19 @@
2715 "domain": domain,2784 "domain": domain,
2716 })2785 })
2717 yield dv.get(timeout=2)2786 yield dv.get(timeout=2)
2718 yield self.assertZoneSerialIncrement(zone_serial)2787 yield self.assertPublicationUpdated()
2719 finally:2788 finally:
2720 yield listener.stopService()2789 yield listener.stopService()
2790 self.assertThat(
2791 self.getCapturedPublication().source, Equals(
2792 "Node %s: domain changed" % node.system_id))
27212793
2722 @wait_for_reactor2794 @wait_for_reactor
2723 @inlineCallbacks2795 @inlineCallbacks
2724 def test_sends_message_for_node_delete(self):2796 def test_sends_message_for_node_delete(self):
2725 yield deferToDatabase(register_system_triggers)2797 yield deferToDatabase(register_system_triggers)
2726 node = yield deferToDatabase(self.create_node)2798 node = yield deferToDatabase(self.create_node)
2727 zone_serial = yield self.get_zone_serial_current()2799 yield self.capturePublication()
2728 dv = DeferredValue()2800 dv = DeferredValue()
2729 listener = self.make_listener_without_delay()2801 listener = self.make_listener_without_delay()
2730 listener.register(2802 listener.register(
@@ -2733,9 +2805,12 @@
2733 try:2805 try:
2734 yield deferToDatabase(self.delete_node, node.system_id)2806 yield deferToDatabase(self.delete_node, node.system_id)
2735 yield dv.get(timeout=2)2807 yield dv.get(timeout=2)
2736 yield self.assertZoneSerialIncrement(zone_serial)2808 yield self.assertPublicationUpdated()
2737 finally:2809 finally:
2738 yield listener.stopService()2810 yield listener.stopService()
2811 self.assertThat(
2812 self.getCapturedPublication().source,
2813 Equals("Call to sys_dns_node_delete"))
27392814
27402815
2741class TestDNSInterfaceListener(2816class TestDNSInterfaceListener(
@@ -2764,20 +2839,26 @@
2764 def test_sends_message_for_interface_update_name(self):2839 def test_sends_message_for_interface_update_name(self):
2765 yield deferToDatabase(register_system_triggers)2840 yield deferToDatabase(register_system_triggers)
2766 interface = yield deferToDatabase(self.create_interface)2841 interface = yield deferToDatabase(self.create_interface)
2767 zone_serial = yield self.get_zone_serial_current()2842 yield self.capturePublication()
2768 dv = DeferredValue()2843 dv = DeferredValue()
2769 listener = self.make_listener_without_delay()2844 listener = self.make_listener_without_delay()
2770 listener.register(2845 listener.register(
2771 "sys_dns", lambda *args: dv.set(args))2846 "sys_dns", lambda *args: dv.set(args))
2772 yield listener.startService()2847 yield listener.startService()
2773 try:2848 try:
2849 name_old = interface.name
2850 name_new = factory.make_name("name")
2774 yield deferToDatabase(self.update_interface, interface.id, {2851 yield deferToDatabase(self.update_interface, interface.id, {
2775 "name": factory.make_name("name"),2852 "name": name_new,
2776 })2853 })
2777 yield dv.get(timeout=2)2854 yield dv.get(timeout=2)
2778 yield self.assertZoneSerialIncrement(zone_serial)2855 yield self.assertPublicationUpdated()
2779 finally:2856 finally:
2780 yield listener.stopService()2857 yield listener.stopService()
2858 self.assertThat(
2859 self.getCapturedPublication().source, Equals(
2860 "Interface %s: renamed from %s to %s"
2861 % (name_new, name_old, name_new)))
27812862
2782 @wait_for_reactor2863 @wait_for_reactor
2783 @inlineCallbacks2864 @inlineCallbacks
@@ -2785,7 +2866,7 @@
2785 yield deferToDatabase(register_system_triggers)2866 yield deferToDatabase(register_system_triggers)
2786 interface = yield deferToDatabase(self.create_unknown_interface)2867 interface = yield deferToDatabase(self.create_unknown_interface)
2787 node = yield deferToDatabase(self.create_node)2868 node = yield deferToDatabase(self.create_node)
2788 zone_serial = yield self.get_zone_serial_current()2869 yield self.capturePublication()
2789 dv = DeferredValue()2870 dv = DeferredValue()
2790 listener = self.make_listener_without_delay()2871 listener = self.make_listener_without_delay()
2791 listener.register(2872 listener.register(
@@ -2795,16 +2876,19 @@
2795 yield deferToDatabase(2876 yield deferToDatabase(
2796 self.migrate_unknown_to_physical, interface.id, node)2877 self.migrate_unknown_to_physical, interface.id, node)
2797 yield dv.get(timeout=2)2878 yield dv.get(timeout=2)
2798 yield self.assertZoneSerialIncrement(zone_serial)2879 yield self.assertPublicationUpdated()
2799 finally:2880 finally:
2800 yield listener.stopService()2881 yield listener.stopService()
2882 self.assertThat(
2883 self.getCapturedPublication().source, Equals(
2884 "Interface %s: node set" % interface.name))
28012885
2802 @wait_for_reactor2886 @wait_for_reactor
2803 @inlineCallbacks2887 @inlineCallbacks
2804 def test_sends_message_for_physical_to_unknown(self):2888 def test_sends_message_for_physical_to_unknown(self):
2805 yield deferToDatabase(register_system_triggers)2889 yield deferToDatabase(register_system_triggers)
2806 interface = yield deferToDatabase(self.create_interface)2890 interface = yield deferToDatabase(self.create_interface)
2807 zone_serial = yield self.get_zone_serial_current()2891 yield self.capturePublication()
2808 dv = DeferredValue()2892 dv = DeferredValue()
2809 listener = self.make_listener_without_delay()2893 listener = self.make_listener_without_delay()
2810 listener.register(2894 listener.register(
@@ -2814,9 +2898,12 @@
2814 yield deferToDatabase(2898 yield deferToDatabase(
2815 self.migrate_physical_to_unknown, interface.id)2899 self.migrate_physical_to_unknown, interface.id)
2816 yield dv.get(timeout=2)2900 yield dv.get(timeout=2)
2817 yield self.assertZoneSerialIncrement(zone_serial)2901 yield self.assertPublicationUpdated()
2818 finally:2902 finally:
2819 yield listener.stopService()2903 yield listener.stopService()
2904 self.assertThat(
2905 self.getCapturedPublication().source, Equals(
2906 "Interface %s: node unset" % interface.name))
28202907
2821 @wait_for_reactor2908 @wait_for_reactor
2822 @inlineCallbacks2909 @inlineCallbacks
@@ -2824,7 +2911,7 @@
2824 yield deferToDatabase(register_system_triggers)2911 yield deferToDatabase(register_system_triggers)
2825 interface = yield deferToDatabase(self.create_interface)2912 interface = yield deferToDatabase(self.create_interface)
2826 node = yield deferToDatabase(self.create_node)2913 node = yield deferToDatabase(self.create_node)
2827 zone_serial = yield self.get_zone_serial_current()2914 yield self.capturePublication()
2828 dv = DeferredValue()2915 dv = DeferredValue()
2829 listener = self.make_listener_without_delay()2916 listener = self.make_listener_without_delay()
2830 listener.register(2917 listener.register(
@@ -2834,9 +2921,12 @@
2834 yield deferToDatabase(2921 yield deferToDatabase(
2835 self.update_interface, interface.id, {"node": node})2922 self.update_interface, interface.id, {"node": node})
2836 yield dv.get(timeout=2)2923 yield dv.get(timeout=2)
2837 yield self.assertZoneSerialIncrement(zone_serial)2924 yield self.assertPublicationUpdated()
2838 finally:2925 finally:
2839 yield listener.stopService()2926 yield listener.stopService()
2927 self.assertThat(
2928 self.getCapturedPublication().source, Equals(
2929 "Interface %s: node changed" % interface.name))
28402930
28412931
2842class TestDNSConfigListener(2932class TestDNSConfigListener(
@@ -2847,8 +2937,9 @@
2847 @wait_for_reactor2937 @wait_for_reactor
2848 @inlineCallbacks2938 @inlineCallbacks
2849 def test_sends_message_for_config_upstream_dns_insert(self):2939 def test_sends_message_for_config_upstream_dns_insert(self):
2940 upstream_dns_new = factory.make_ip_address()
2850 yield deferToDatabase(register_system_triggers)2941 yield deferToDatabase(register_system_triggers)
2851 zone_serial = yield self.get_zone_serial_current()2942 yield self.capturePublication()
2852 dv = DeferredValue()2943 dv = DeferredValue()
2853 listener = self.make_listener_without_delay()2944 listener = self.make_listener_without_delay()
2854 listener.register(2945 listener.register(
@@ -2857,17 +2948,22 @@
2857 try:2948 try:
2858 yield deferToDatabase(2949 yield deferToDatabase(
2859 Config.objects.set_config,2950 Config.objects.set_config,
2860 "upstream_dns", factory.make_ip_address())2951 "upstream_dns", upstream_dns_new)
2861 yield dv.get(timeout=2)2952 yield dv.get(timeout=2)
2862 yield self.assertZoneSerialIncrement(zone_serial)2953 yield self.assertPublicationUpdated()
2863 finally:2954 finally:
2864 yield listener.stopService()2955 yield listener.stopService()
2956 self.assertThat(
2957 self.getCapturedPublication().source, Equals(
2958 "Configuration upstream_dns set to %s"
2959 % json.dumps(upstream_dns_new)))
28652960
2866 @wait_for_reactor2961 @wait_for_reactor
2867 @inlineCallbacks2962 @inlineCallbacks
2868 def test_sends_message_for_config_default_dns_ttl_insert(self):2963 def test_sends_message_for_config_default_dns_ttl_insert(self):
2964 default_dns_ttl_new = random.randint(10, 1000)
2869 yield deferToDatabase(register_system_triggers)2965 yield deferToDatabase(register_system_triggers)
2870 zone_serial = yield self.get_zone_serial_current()2966 yield self.capturePublication()
2871 dv = DeferredValue()2967 dv = DeferredValue()
2872 listener = self.make_listener_without_delay()2968 listener = self.make_listener_without_delay()
2873 listener.register(2969 listener.register(
@@ -2876,17 +2972,22 @@
2876 try:2972 try:
2877 yield deferToDatabase(2973 yield deferToDatabase(
2878 Config.objects.set_config,2974 Config.objects.set_config,
2879 "default_dns_ttl", random.randint(10, 1000))2975 "default_dns_ttl", default_dns_ttl_new)
2880 yield dv.get(timeout=2)2976 yield dv.get(timeout=2)
2881 yield self.assertZoneSerialIncrement(zone_serial)2977 yield self.assertPublicationUpdated()
2882 finally:2978 finally:
2883 yield listener.stopService()2979 yield listener.stopService()
2980 self.assertThat(
2981 self.getCapturedPublication().source, Equals(
2982 "Configuration default_dns_ttl set to %s"
2983 % json.dumps(default_dns_ttl_new)))
28842984
2885 @wait_for_reactor2985 @wait_for_reactor
2886 @inlineCallbacks2986 @inlineCallbacks
2887 def test_sends_message_for_config_windows_kms_host_insert(self):2987 def test_sends_message_for_config_windows_kms_host_insert(self):
2988 kms_host_new = factory.make_name("kms-host-new")
2888 yield deferToDatabase(register_system_triggers)2989 yield deferToDatabase(register_system_triggers)
2889 zone_serial = yield self.get_zone_serial_current()2990 yield self.capturePublication()
2890 dv = DeferredValue()2991 dv = DeferredValue()
2891 listener = self.make_listener_without_delay()2992 listener = self.make_listener_without_delay()
2892 listener.register(2993 listener.register(
@@ -2895,20 +2996,26 @@
2895 try:2996 try:
2896 yield deferToDatabase(2997 yield deferToDatabase(
2897 Config.objects.set_config,2998 Config.objects.set_config,
2898 "windows_kms_host", factory.make_name("kms"))2999 "windows_kms_host", kms_host_new)
2899 yield dv.get(timeout=2)3000 yield dv.get(timeout=2)
2900 yield self.assertZoneSerialIncrement(zone_serial)3001 yield self.assertPublicationUpdated()
2901 finally:3002 finally:
2902 yield listener.stopService()3003 yield listener.stopService()
3004 self.assertThat(
3005 self.getCapturedPublication().source, Equals(
3006 "Configuration windows_kms_host set to %s"
3007 % json.dumps(kms_host_new)))
29033008
2904 @wait_for_reactor3009 @wait_for_reactor
2905 @inlineCallbacks3010 @inlineCallbacks
2906 def test_sends_message_for_config_upstream_dns_update(self):3011 def test_sends_message_for_config_upstream_dns_update(self):
3012 upstream_dns_old = factory.make_ip_address()
3013 upstream_dns_new = factory.make_ip_address()
2907 yield deferToDatabase(register_system_triggers)3014 yield deferToDatabase(register_system_triggers)
2908 yield deferToDatabase(3015 yield deferToDatabase(
2909 Config.objects.set_config,3016 Config.objects.set_config,
2910 "upstream_dns", factory.make_ip_address())3017 "upstream_dns", upstream_dns_old)
2911 zone_serial = yield self.get_zone_serial_current()3018 yield self.capturePublication()
2912 dv = DeferredValue()3019 dv = DeferredValue()
2913 listener = self.make_listener_without_delay()3020 listener = self.make_listener_without_delay()
2914 listener.register(3021 listener.register(
@@ -2917,20 +3024,27 @@
2917 try:3024 try:
2918 yield deferToDatabase(3025 yield deferToDatabase(
2919 Config.objects.set_config,3026 Config.objects.set_config,
2920 "upstream_dns", factory.make_ip_address())3027 "upstream_dns", upstream_dns_new)
2921 yield dv.get(timeout=2)3028 yield dv.get(timeout=2)
2922 yield self.assertZoneSerialIncrement(zone_serial)3029 yield self.assertPublicationUpdated()
2923 finally:3030 finally:
2924 yield listener.stopService()3031 yield listener.stopService()
3032 self.assertThat(
3033 self.getCapturedPublication().source, Equals(
3034 "Configuration upstream_dns changed from %s to %s"
3035 % (json.dumps(upstream_dns_old),
3036 json.dumps(upstream_dns_new))))
29253037
2926 @wait_for_reactor3038 @wait_for_reactor
2927 @inlineCallbacks3039 @inlineCallbacks
2928 def test_sends_message_for_config_default_dns_ttl_update(self):3040 def test_sends_message_for_config_default_dns_ttl_update(self):
3041 default_dns_ttl_old = random.randint(10, 1000)
3042 default_dns_ttl_new = random.randint(10, 1000)
2929 yield deferToDatabase(register_system_triggers)3043 yield deferToDatabase(register_system_triggers)
2930 yield deferToDatabase(3044 yield deferToDatabase(
2931 Config.objects.set_config,3045 Config.objects.set_config,
2932 "default_dns_ttl", random.randint(10, 1000))3046 "default_dns_ttl", default_dns_ttl_old)
2933 zone_serial = yield self.get_zone_serial_current()3047 yield self.capturePublication()
2934 dv = DeferredValue()3048 dv = DeferredValue()
2935 listener = self.make_listener_without_delay()3049 listener = self.make_listener_without_delay()
2936 listener.register(3050 listener.register(
@@ -2939,20 +3053,27 @@
2939 try:3053 try:
2940 yield deferToDatabase(3054 yield deferToDatabase(
2941 Config.objects.set_config,3055 Config.objects.set_config,
2942 "default_dns_ttl", random.randint(10, 1000))3056 "default_dns_ttl", default_dns_ttl_new)
2943 yield dv.get(timeout=2)3057 yield dv.get(timeout=2)
2944 yield self.assertZoneSerialIncrement(zone_serial)3058 yield self.assertPublicationUpdated()
2945 finally:3059 finally:
2946 yield listener.stopService()3060 yield listener.stopService()
3061 self.assertThat(
3062 self.getCapturedPublication().source, Equals(
3063 "Configuration default_dns_ttl changed from %s to %s"
3064 % (json.dumps(default_dns_ttl_old),
3065 json.dumps(default_dns_ttl_new))))
29473066
2948 @wait_for_reactor3067 @wait_for_reactor
2949 @inlineCallbacks3068 @inlineCallbacks
2950 def test_sends_message_for_config_windows_kms_host_update(self):3069 def test_sends_message_for_config_windows_kms_host_update(self):
3070 kms_host_old = factory.make_name("kms-host-old")
3071 kms_host_new = factory.make_name("kms-host-new")
2951 yield deferToDatabase(register_system_triggers)3072 yield deferToDatabase(register_system_triggers)
2952 yield deferToDatabase(3073 yield deferToDatabase(
2953 Config.objects.set_config,3074 Config.objects.set_config,
2954 "windows_kms_host", factory.make_name("kms"))3075 "windows_kms_host", kms_host_old)
2955 zone_serial = yield self.get_zone_serial_current()3076 yield self.capturePublication()
2956 dv = DeferredValue()3077 dv = DeferredValue()
2957 listener = self.make_listener_without_delay()3078 listener = self.make_listener_without_delay()
2958 listener.register(3079 listener.register(
@@ -2961,11 +3082,15 @@
2961 try:3082 try:
2962 yield deferToDatabase(3083 yield deferToDatabase(
2963 Config.objects.set_config,3084 Config.objects.set_config,
2964 "windows_kms_host", factory.make_name("kms"))3085 "windows_kms_host", kms_host_new)
2965 yield dv.get(timeout=2)3086 yield dv.get(timeout=2)
2966 yield self.assertZoneSerialIncrement(zone_serial)3087 yield self.assertPublicationUpdated()
2967 finally:3088 finally:
2968 yield listener.stopService()3089 yield listener.stopService()
3090 self.assertThat(
3091 self.getCapturedPublication().source, Equals(
3092 "Configuration windows_kms_host changed from %s to %s"
3093 % (json.dumps(kms_host_old), json.dumps(kms_host_new))))
29693094
29703095
2971class TestProxySubnetListener(3096class TestProxySubnetListener(
29723097
=== modified file 'src/maasserver/triggers/tests/test_websocket_listener.py'
--- src/maasserver/triggers/tests/test_websocket_listener.py 2016-04-11 16:23:26 +0000
+++ src/maasserver/triggers/tests/test_websocket_listener.py 2016-04-29 11:37:21 +0000
@@ -20,7 +20,7 @@
20from maasserver.models.partition import MIN_PARTITION_SIZE20from maasserver.models.partition import MIN_PARTITION_SIZE
21from maasserver.testing.factory import factory21from maasserver.testing.factory import factory
22from maasserver.testing.testcase import MAASTransactionServerTestCase22from maasserver.testing.testcase import MAASTransactionServerTestCase
23from maasserver.triggers.tests.helper import TransactionalHelpersMixin23from maasserver.triggers.testing import TransactionalHelpersMixin
24from maasserver.triggers.websocket import register_websocket_triggers24from maasserver.triggers.websocket import register_websocket_triggers
25from maasserver.utils.threads import deferToDatabase25from maasserver.utils.threads import deferToDatabase
26from provisioningserver.utils.twisted import (26from provisioningserver.utils.twisted import (