Merge ~r00ta/maas:lp-2046255-fix-multiple-dns-A-records-for-host into maas:master

Proposed by Jacopo Rota
Status: Merged
Approved by: Jacopo Rota
Approved revision: 591c841f18c425307ac58aa0969a3a5bc96fab62
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~r00ta/maas:lp-2046255-fix-multiple-dns-A-records-for-host
Merge into: maas:master
Diff against target: 1146 lines (+630/-79)
8 files modified
src/maasserver/region_controller.py (+25/-5)
src/maasserver/tests/test_region_controller.py (+11/-6)
src/maasserver/triggers/models/__init__.py (+0/-0)
src/maasserver/triggers/models/dns_notifications.py (+51/-0)
src/maasserver/triggers/models/tests/__init__.py (+0/-0)
src/maasserver/triggers/models/tests/test_dns_notifications.py (+43/-0)
src/maasserver/triggers/system.py (+271/-33)
src/maasserver/triggers/tests/test_system.py (+229/-35)
Reviewer Review Type Date Requested Status
MAAS Lander Approve
Christian Grabowski Approve
Review via email: mp+457612@code.launchpad.net

Commit message

fix: lp-2046255 IP addresses of non-boot interfaces should not add A records to <machine>.<domain>

Description of the change

non-boot interfaces should not add A records to <machine>.<domain>.

In addition to that, this MP makes every postgres notification unique because postgres drops duplicated notifications that are performed within the same transaction.
In our case when we perform operations on our entities (for example an interface), we execute multiple triggers within the same transaction and we must be sure that all the notifications are sent (the order is important).

To post a comment you must log in.
12a3152... by Jacopo Rota

fix: lp-2046255 IP addresses of non-boot interfaces should not add A records to <machine>.<domain>

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lp-2046255-fix-multiple-dns-A-records-for-host lp:~r00ta/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci.internal:8080/job/maas-tester/4250/console
COMMIT: 7ef226238eada3d5684a818261ff68cd3a909b84

review: Needs Fixing
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lp-2046255-fix-multiple-dns-A-records-for-host lp:~r00ta/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 12a31525598d6d94921f61bce58316822752da5b

review: Approve
Revision history for this message
Christian Grabowski (cgrabowski) wrote :

+1

review: Approve
591c841... by Jacopo Rota

add missing __init__ to new module

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lp-2046255-fix-multiple-dns-A-records-for-host lp:~r00ta/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 591c841f18c425307ac58aa0969a3a5bc96fab62

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/region_controller.py b/src/maasserver/region_controller.py
2index 756dfbd..7cc6428 100644
3--- a/src/maasserver/region_controller.py
4+++ b/src/maasserver/region_controller.py
5@@ -53,6 +53,9 @@ from maasserver.proxyconfig import proxy_update_config
6 from maasserver.rbac import RBACClient, Resource, SyncConflictError
7 from maasserver.secrets import SecretManager
8 from maasserver.service_monitor import service_monitor
9+from maasserver.triggers.models.dns_notifications import (
10+ DynamicDNSUpdateNotification,
11+)
12 from maasserver.utils import synchronised
13 from maasserver.utils.orm import transactional, with_connection
14 from maasserver.utils.threads import deferToDatabase
15@@ -181,12 +184,24 @@ class RegionControllerService(Service):
16 """
17 Called when the `sys_dns_update` message is received
18 and queues updates for existing domains.
19+ Since an uncatched exception would stop the processing of the notifications, we catch and log
20+ every exception at top level.
21+ """
22+ try:
23+ return self._queueDynamicDNSUpdate(channel, message)
24+ except Exception as e:
25+ log.warn(
26+ f"The message '{message}' might not have been processed correctly due to the exception: '{e}'"
27+ )
28+
29+ def _queueDynamicDNSUpdate(self, channel, message):
30+ """
31 The updates are offloaded to the DatabaseTasksService in order to
32 process them in sequence and keep consuming the next postgres notifications.
33 """
34
35- def updateCallback(data):
36- (new_updates, need_reload) = data
37+ def updateCallback(result):
38+ (new_updates, need_reload) = result
39 self._dns_requires_full_reload = (
40 self._dns_requires_full_reload or need_reload
41 )
42@@ -195,14 +210,19 @@ class RegionControllerService(Service):
43 else:
44 self._dns_updates += new_updates
45
46- log.debug("Start processing dynamic DNS update '{}'".format(message))
47- if message == "":
48+ log.debug(f"Start processing dynamic DNS update '{message}'")
49+
50+ notification = DynamicDNSUpdateNotification(message)
51+ if not notification.is_valid():
52+ log.warn(
53+ f"The dynamic dns update notification '{message}' is not valid. It will be dropped."
54+ )
55 return
56
57 self.dbtasks.deferTaskWithCallbacks(
58 process_dns_update_notify,
59 [updateCallback],
60- message,
61+ notification.get_decoded_message(),
62 )
63
64 def startProcessing(self):
65diff --git a/src/maasserver/tests/test_region_controller.py b/src/maasserver/tests/test_region_controller.py
66index b688e58..0b45912 100644
67--- a/src/maasserver/tests/test_region_controller.py
68+++ b/src/maasserver/tests/test_region_controller.py
69@@ -866,7 +866,7 @@ class TestRegionControllerServiceTransactional(MAASTransactionServerTestCase):
70 service._dns_update_in_progress = True
71 service.queueDynamicDNSUpdate(
72 factory.make_name(),
73- f"INSERT {domain.name} {record.name} A 30 10.10.10.10",
74+ f"b473ff04d60c0d2af08cb5c342d815f8 INSERT {domain.name} {record.name} A 30 10.10.10.10",
75 )
76
77 # Wait until all the dynamic updates are processed
78@@ -921,7 +921,7 @@ class TestRegionControllerServiceTransactional(MAASTransactionServerTestCase):
79 service._dns_update_in_progress = True
80 service.queueDynamicDNSUpdate(
81 factory.make_name(),
82- f"INSERT {domain.name} {record.name} A 30 10.10.10.10",
83+ f"3108394140029edf0bdbffa68bb29556 INSERT {domain.name} {record.name} A 30 10.10.10.10",
84 )
85
86 # Wait until all the dynamic updates are processed
87@@ -997,19 +997,24 @@ class TestRegionControllerServiceTransactional(MAASTransactionServerTestCase):
88 for _ in range(3):
89 service.queueDynamicDNSUpdate(
90 factory.make_name(),
91- f"INSERT {domain.name} {record1.name} A 30 1.1.1.1",
92+ f"3108394140029edf0bdbffa68bb29556 INSERT {domain.name} {record1.name} A 30 1.1.1.1",
93 )
94 service.queueDynamicDNSUpdate(
95 factory.make_name(),
96- f"INSERT {domain.name} {record2.name} A 30 2.2.2.2",
97+ f"6fa241096ebabe34660c437fb0627b61 INSERT {domain.name} {record2.name} A 30 2.2.2.2",
98 )
99+ # An invalid message that should be ignored
100 service.queueDynamicDNSUpdate(
101 factory.make_name(),
102- f"DELETE {domain.name} {record1.name} A 30 1.1.1.1",
103+ f"INSERT {domain.name} {record2.name} A 30 10.2.2.2",
104 )
105 service.queueDynamicDNSUpdate(
106 factory.make_name(),
107- f"DELETE {domain.name} {record2.name} A 30 2.2.2.2",
108+ f"640f180ba9064411f30a3d5c587e86cc DELETE {domain.name} {record1.name} A 30 1.1.1.1",
109+ )
110+ service.queueDynamicDNSUpdate(
111+ factory.make_name(),
112+ f"f53dcb0704c211e6f747b95b0ea3e128 DELETE {domain.name} {record2.name} A 30 2.2.2.2",
113 )
114
115 # Wait until all the dynamic updates are processed
116diff --git a/src/maasserver/triggers/models/__init__.py b/src/maasserver/triggers/models/__init__.py
117new file mode 100644
118index 0000000..e69de29
119--- /dev/null
120+++ b/src/maasserver/triggers/models/__init__.py
121diff --git a/src/maasserver/triggers/models/dns_notifications.py b/src/maasserver/triggers/models/dns_notifications.py
122new file mode 100644
123index 0000000..46fcaff
124--- /dev/null
125+++ b/src/maasserver/triggers/models/dns_notifications.py
126@@ -0,0 +1,51 @@
127+from re import compile
128+
129+
130+class NotValidDNSNotificationPayload(Exception):
131+ """Error raised when the dynamic dns update notification is not valid."""
132+
133+
134+class DynamicDNSUpdateNotification:
135+ """
136+ Postgres drops duplicated notifications fired in the same transaction.
137+ Since we are in this case for the dns notifications, we have to ensure that our notifications are unique.
138+ This is why the notification payload is in the format
139+ <random md5 string> <message>
140+ """
141+
142+ # Match MD5 string + white space.
143+ DYNAMIC_DNS_PREFIX_PATTERN = compile(r"^[0-9a-f]{32}\s*")
144+
145+ def __init__(self, payload: str):
146+ self.payload = payload
147+ # cache the plain message the first time we parse it
148+ self.decoded_message = None
149+
150+ def is_valid(self) -> bool:
151+ """
152+ Checks if the payload is a valid dynamic dns update message.
153+ If it's valid, the result is cached.
154+ """
155+ if self.decoded_message:
156+ return True
157+
158+ match = self.DYNAMIC_DNS_PREFIX_PATTERN.match(self.payload)
159+ if not match:
160+ return False
161+
162+ # Remove the prefix and cache the result
163+ self.decoded_message = self.payload[len(match.group(0)) :]
164+ return True
165+
166+ def get_decoded_message(self) -> str:
167+ if self.decoded_message:
168+ return self.decoded_message
169+
170+ # Check if it's valid and store the decoded message
171+ if not self.is_valid():
172+ raise NotValidDNSNotificationPayload(
173+ "Message '%s' is not a valid dynamic dns update."
174+ % self.payload
175+ )
176+
177+ return self.decoded_message
178diff --git a/src/maasserver/triggers/models/tests/__init__.py b/src/maasserver/triggers/models/tests/__init__.py
179new file mode 100644
180index 0000000..e69de29
181--- /dev/null
182+++ b/src/maasserver/triggers/models/tests/__init__.py
183diff --git a/src/maasserver/triggers/models/tests/test_dns_notifications.py b/src/maasserver/triggers/models/tests/test_dns_notifications.py
184new file mode 100644
185index 0000000..0119d40
186--- /dev/null
187+++ b/src/maasserver/triggers/models/tests/test_dns_notifications.py
188@@ -0,0 +1,43 @@
189+import pytest
190+
191+from maasserver.triggers.models.dns_notifications import (
192+ DynamicDNSUpdateNotification,
193+ NotValidDNSNotificationPayload,
194+)
195+from maastesting.testcase import MAASTestCase
196+
197+
198+class TestDynamicDNSUpdateNotification(MAASTestCase):
199+ def test_is_valid(self):
200+ valid_payloads = [
201+ "5470596195891583856bdf849f2acfda RELOAD",
202+ "bedf5c93d7e7b82d45cfc555630cb8b1 INSERT maas taillow A 0 10.246.64.208",
203+ "adfbc39c6213a062afb584b90d508f52 DELETE mydomain mudkip A 192.168.33.163",
204+ ]
205+ for valid_payload in valid_payloads:
206+ notification = DynamicDNSUpdateNotification(valid_payload)
207+ assert notification.is_valid()
208+
209+ def test_invalid_payloads(self):
210+ invalid_payloads = [
211+ "",
212+ "5470596195891583849f2acfda RELOAD",
213+ "ADFBC39c6213a062afb584B90D508F52 DELETE mydomain mudkip A 192.168.33.163",
214+ ]
215+ for invalid_payload in invalid_payloads:
216+ notification = DynamicDNSUpdateNotification(invalid_payload)
217+ assert not notification.is_valid()
218+
219+ def test_get_decoded_message(self):
220+ payload = "adfbc39c6213a062afb584b90d508f52 DELETE mydomain mudkip A 192.168.33.163"
221+ notification = DynamicDNSUpdateNotification(payload)
222+ assert (
223+ notification.get_decoded_message()
224+ == "DELETE mydomain mudkip A 192.168.33.163"
225+ )
226+
227+ def test_get_decoded_message_if_invalid(self):
228+ payload = ""
229+ notification = DynamicDNSUpdateNotification(payload)
230+ with pytest.raises(NotValidDNSNotificationPayload):
231+ notification.get_decoded_message()
232diff --git a/src/maasserver/triggers/system.py b/src/maasserver/triggers/system.py
233index bfd514a..b8110c8 100644
234--- a/src/maasserver/triggers/system.py
235+++ b/src/maasserver/triggers/system.py
236@@ -285,6 +285,211 @@ CORE_REGIONRACKRPCONNECTION_DELETE = dedent(
237 """
238 )
239
240+CORE_GEN_RANDOM_PREFIX = dedent(
241+ """\
242+ CREATE OR REPLACE FUNCTION gen_random_prefix() RETURNS TEXT AS $$
243+ DECLARE
244+ result text;
245+ BEGIN
246+ result := md5(random()::text);
247+ RETURN result;
248+ END;
249+ $$ LANGUAGE plpgsql;
250+ """
251+)
252+
253+CORE_UPDATE_DATA_DNS_NOTIFICATION_FORMAT = dedent(
254+ """\
255+ CREATE OR REPLACE FUNCTION update_data_dns_notification(
256+ id BIGINT
257+ ) RETURNS TEXT AS $$
258+ DECLARE
259+ result text;
260+ BEGIN
261+ result := gen_random_prefix() || ' UPDATE-DATA ' || id;
262+ RETURN result;
263+ END;
264+ $$ LANGUAGE plpgsql;
265+ """
266+)
267+
268+CORE_INSERT_DATA_DNS_NOTIFICATION_FORMAT = dedent(
269+ """\
270+ CREATE OR REPLACE FUNCTION insert_data_dns_notification(
271+ id BIGINT
272+ ) RETURNS TEXT AS $$
273+ DECLARE
274+ result text;
275+ BEGIN
276+ result := gen_random_prefix() || ' INSERT-DATA ' || id;
277+ RETURN result;
278+ END;
279+ $$ LANGUAGE plpgsql;
280+ """
281+)
282+
283+CORE_DELETE_IP_DNS_NOTIFICATION_FORMAT = dedent(
284+ """\
285+ CREATE OR REPLACE FUNCTION delete_ip_dns_notification(
286+ domain text,
287+ rname text,
288+ rtype text
289+ ) RETURNS TEXT AS $$
290+ DECLARE
291+ result text;
292+ BEGIN
293+ result := gen_random_prefix() || ' DELETE-IP ' || domain || ' ' || rname || ' ' || rtype;
294+ RETURN result;
295+ END;
296+ $$ LANGUAGE plpgsql;
297+ """
298+)
299+
300+CORE_DELETE_IFACE_IP_DNS_NOTIFICATION_FORMAT = dedent(
301+ """\
302+ CREATE OR REPLACE FUNCTION delete_iface_ip_dns_notification(
303+ domain text,
304+ current_hostname text,
305+ rtype text,
306+ interface_id text
307+ ) RETURNS TEXT AS $$
308+ DECLARE
309+ result text;
310+ BEGIN
311+ result := gen_random_prefix() || ' DELETE-IFACE-IP ' || domain || ' ' || current_hostname || ' ' || rtype || ' ' || interface_id;
312+ RETURN result;
313+ END;
314+ $$ LANGUAGE plpgsql;
315+ """
316+)
317+
318+CORE_BOOT_INTERFACE_INSERT_DNS_NOTIFICATION_FORMAT = dedent(
319+ """\
320+ CREATE OR REPLACE FUNCTION insert_boot_interface_dns_notification(
321+ domain text,
322+ current_hostname text,
323+ address_ttl INT,
324+ ip_address text
325+ ) RETURNS TEXT AS $$
326+ DECLARE
327+ result text;
328+ BEGIN
329+ result := gen_random_prefix() || ' INSERT ' || domain || ' ' || current_hostname || ' A ' || address_ttl || ' ' || ip_address;
330+ RETURN result;
331+ END;
332+ $$ LANGUAGE plpgsql;
333+ """
334+)
335+
336+CORE_NON_BOOT_INTERFACE_INSERT_DNS_NOTIFICATION_FORMAT = dedent(
337+ """\
338+ CREATE OR REPLACE FUNCTION insert_non_boot_interface_dns_notification(
339+ domain text,
340+ iface_name text,
341+ current_hostname text,
342+ address_ttl INT,
343+ ip_address text
344+ ) RETURNS TEXT AS $$
345+ DECLARE
346+ result text;
347+ BEGIN
348+ result := gen_random_prefix() || ' INSERT ' || domain || ' ' || iface_name || '.' || current_hostname || ' A ' || address_ttl || ' ' || ip_address;
349+ RETURN result;
350+ END;
351+ $$ LANGUAGE plpgsql;
352+ """
353+)
354+
355+CORE_BOOT_INTERFACE_DELETE_DNS_NOTIFICATION_FORMAT = dedent(
356+ """\
357+ CREATE OR REPLACE FUNCTION delete_boot_interface_dns_notification(
358+ domain text,
359+ current_hostname text,
360+ ip_address text
361+ ) RETURNS TEXT AS $$
362+ DECLARE
363+ result text;
364+ BEGIN
365+ result := gen_random_prefix() || ' DELETE ' || domain || ' ' || current_hostname || ' A';
366+ IF ip_address IS NOT NULL AND ip_address != '' THEN
367+ result := result || ' ' || ip_address;
368+ END IF;
369+ RETURN result;
370+ END;
371+ $$ LANGUAGE plpgsql;
372+ """
373+)
374+
375+CORE_DELETE_DNS_NOTIFICATION_FORMAT = dedent(
376+ """\
377+ CREATE OR REPLACE FUNCTION delete_dns_notification(
378+ domain text,
379+ current_hostname text,
380+ rtype text
381+ ) RETURNS TEXT AS $$
382+ DECLARE
383+ result text;
384+ BEGIN
385+ result := gen_random_prefix() || ' DELETE ' || domain || ' ' || current_hostname || ' ' || rtype;
386+ RETURN result;
387+ END;
388+ $$ LANGUAGE plpgsql;
389+ """
390+)
391+
392+CORE_NON_BOOT_INTERFACE_DELETE_DNS_NOTIFICATION_FORMAT = dedent(
393+ """\
394+ CREATE OR REPLACE FUNCTION delete_non_boot_interface_dns_notification(
395+ domain text,
396+ iface_name text,
397+ current_hostname text,
398+ ip_address text
399+ ) RETURNS TEXT AS $$
400+ DECLARE
401+ result text;
402+ BEGIN
403+ result := gen_random_prefix() || ' DELETE ' || domain || ' ' || iface_name || '.' || current_hostname || ' A';
404+ IF ip_address IS NOT NULL AND ip_address != '' THEN
405+ result := result || ' ' || ip_address;
406+ END IF;
407+ RETURN result;
408+ END;
409+ $$ LANGUAGE plpgsql;
410+ """
411+)
412+
413+CORE_BOOT_INTERFACE_UPDATE_DNS_NOTIFICATION_FORMAT = dedent(
414+ """\
415+ CREATE OR REPLACE FUNCTION update_boot_interface_dns_notification(
416+ domain text,
417+ current_hostname text,
418+ address_ttl INT,
419+ ip_address text
420+ ) RETURNS TEXT AS $$
421+ DECLARE
422+ result text;
423+ BEGIN
424+ result := gen_random_prefix() || ' UPDATE ' || domain || ' ' || current_hostname || ' A ' || address_ttl || ' ' || ip_address;
425+ RETURN result;
426+ END;
427+ $$ LANGUAGE plpgsql;
428+ """
429+)
430+
431+CORE_RELOAD_DNS_NOTIFICATION_FORMAT = dedent(
432+ """\
433+ CREATE OR REPLACE FUNCTION reload_dns_notification()
434+ RETURNS TEXT AS $$
435+ DECLARE
436+ result text;
437+ BEGIN
438+ result := gen_random_prefix() || ' RELOAD';
439+ RETURN result;
440+ END;
441+ $$ LANGUAGE plpgsql;
442+ """
443+)
444+
445 # Triggered when the VLAN is modified. When DHCP is turned off/on it will alert
446 # the primary/secondary rack controller to update. If the primary rack or
447 # secondary rack is changed it will alert the previous and new rack controller.
448@@ -1922,19 +2127,19 @@ def render_dns_dynamic_update_dnsresource_ip_addresses_procedure(op):
449 SELECT host(ip) INTO ip_addr FROM maasserver_staticipaddress WHERE id=NEW.staticipaddress_id;
450 SELECT name, domain_id, COALESCE(address_ttl, 0) INTO rname, rdomain_id, ttl FROM maasserver_dnsresource WHERE id=NEW.dnsresource_id;
451 SELECT name INTO domain FROM maasserver_domain WHERE id=rdomain_id;
452- PERFORM pg_notify('sys_dns_updates', 'INSERT ' || domain || ' ' || rname || ' A ' || ttl || ' ' || ip_addr);
453+ PERFORM pg_notify('sys_dns_updates', insert_boot_interface_dns_notification(domain, rname, ttl, ip_addr));
454 ELSIF (TG_OP = 'DELETE' AND TG_LEVEl = 'ROW') THEN
455 IF EXISTS(SELECT id FROM maasserver_dnsresource WHERE id=OLD.dnsresource_id) THEN
456 IF EXISTS(SELECT id FROM maasserver_staticipaddress WHERE id=OLD.staticipaddress_id) THEN
457 SELECT host(ip) INTO ip_addr FROM maasserver_staticipaddress WHERE id=OLD.staticipaddress_id;
458 SELECT name, domain_id INTO rname, rdomain_id FROM maasserver_dnsresource WHERE id=OLD.dnsresource_id;
459 SELECT name INTO domain FROM maasserver_domain WHERE id=rdomain_id;
460- PERFORM pg_notify('sys_dns_updates', 'DELETE ' || domain || ' ' || rname || ' A ' || ip_addr);
461+ PERFORM pg_notify('sys_dns_updates', delete_boot_interface_dns_notification(domain, rname, ip_addr));
462 ELSE
463 SELECT name, domain_id INTO rname, rdomain_id FROM maasserver_dnsresource WHERE id=NEW.dnsresource_id;
464 SELECT name INTO domain FROM maasserver_domain WHERE id=rdomain_id;
465- PERFORM pg_notify('sys_dns_updates', 'DELETE-IP ' || domain || ' ' || rname || ' A');
466- PERFORM pg_notify('sys_dns_updates', 'DELETE-IP ' || domain || ' ' || rname || ' AAAA');
467+ PERFORM pg_notify('sys_dns_updates', delete_ip_dns_notification(domain, rname, 'A'));
468+ PERFORM pg_notify('sys_dns_updates', delete_ip_dns_notification(domain, rname, 'AAAA'));
469 END IF;
470 END IF;
471 END IF;
472@@ -1969,10 +2174,10 @@ def render_dns_dynamic_update_dnsresource_procedure(op):
473 FOREACH ip_addr IN ARRAY ips
474 LOOP
475 IF OLD.name <> NEW.name THEN
476- PERFORM pg_notify('sys_dns_updates', 'DELETE ' || domain || ' ' || OLD.name || ' A ' || ip_addr);
477- PERFORM pg_notify('sys_dns_updates', 'INSERT ' || domain || ' ' || NEW.name || ' A ' || NEW.address_ttl || ' ' || ip_addr);
478+ PERFORM pg_notify('sys_dns_updates', delete_boot_interface_dns_notification(domain, OLD.name, ip_addr));
479+ PERFORM pg_notify('sys_dns_updates', insert_boot_interface_dns_notification(domain, NEW.name, NEW.address_ttl, ip_addr));
480 ELSE
481- PERFORM pg_notify('sys_dns_updates', 'UPDATE ' || domain || ' ' || NEW.name || ' A ' || NEW.address_ttl || ' ' || ip_addr);
482+ PERFORM pg_notify('sys_dns_updates', update_boot_interface_dns_notification(domain, NEW.name, NEW.address_ttl, ip_addr));
483 END IF;
484 END LOOP;
485 END IF;
486@@ -1981,7 +2186,7 @@ def render_dns_dynamic_update_dnsresource_procedure(op):
487 END IF;
488 ELSIF (TG_OP = 'DELETE' AND TG_LEVEL = 'ROW') THEN
489 SELECT name INTO domain FROM maasserver_domain WHERE id=NEW.domain_id;
490- PERFORM pg_notify('sys_dns_updates', 'DELETE ' || domain || ' ' || OLD.name || ' A');
491+ PERFORM pg_notify('sys_dns_updates', delete_boot_interface_dns_notification(domain, OLD.name, ''));
492 END IF;
493 RETURN NULL;
494 END;
495@@ -2005,16 +2210,16 @@ def render_dns_dynamic_update_dnsdata_procedure(op):
496 ASSERT TG_LEVEL <> 'STATEMENT', 'Should not be used as a STATEMENT level trigger', TG_NAME;
497 IF (TG_OP = 'UPDATE' AND TG_LEVEL = 'ROW') THEN
498 IF NEW IS DISTINCT FROM OLD THEN
499- PERFORM pg_notify('sys_dns_updates', 'UPDATE-DATA ' || NEW.id);
500+ PERFORM pg_notify('sys_dns_updates', update_data_dns_notification(NEW.id));
501 ELSE
502 RETURN NULL;
503 END IF;
504 ELSIF (TG_OP = 'INSERT' AND TG_LEVEL = 'ROW') THEN
505- PERFORM pg_notify('sys_dns_updates', 'INSERT-DATA ' || NEW.id);
506+ PERFORM pg_notify('sys_dns_updates', insert_data_dns_notification(NEW.id));
507 ELSIF (TG_OP = 'DELETE' AND TG_LEVEL = 'ROW') THEN
508 SELECT name, domain_id INTO rname, rdomain_id from maasserver_dnsresource WHERE id=OLD.dnsresource_id;
509 SELECT name INTO domain FROM maasserver_domain WHERE id=rdomain_id;
510- PERFORM pg_notify('sys_dns_updates', 'DELETE ' || domain || ' ' || rname || ' ' || OLD.rrtype);
511+ PERFORM pg_notify('sys_dns_updates', delete_dns_notification(domain, rname, OLD.rrtype));
512 END IF;
513 RETURN NULL;
514 END;
515@@ -2031,7 +2236,7 @@ def render_dns_dynamic_update_domain_procedure(op):
516 BEGIN
517 ASSERT TG_WHEN = 'AFTER', 'May only run as an AFTER trigger';
518 ASSERT TG_LEVEL <> 'STATEMENT', 'Should not be used as a STATEMENT level trigger', TG_NAME;
519- PERFORM pg_notify('sys_dns_updates', 'RELOAD');
520+ PERFORM pg_notify('sys_dns_updates', reload_dns_notification());
521 RETURN NULL;
522 END;
523 $$ LANGUAGE plpgsql;
524@@ -2047,7 +2252,7 @@ def render_dns_dynamic_update_subnet_procedure(op):
525 BEGIN
526 ASSERT TG_WHEN = 'AFTER', 'May only run as an AFTER trigger';
527 ASSERT TG_LEVEL <> 'STATEMENT', 'Should not be used as a STATEMENT level trigger', TG_NAME;
528- PERFORM pg_notify('sys_dns_updates', 'RELOAD');
529+ PERFORM pg_notify('sys_dns_updates', reload_dns_notification());
530 RETURN NULL;
531 END;
532 $$ LANGUAGE plpgsql;
533@@ -2095,9 +2300,9 @@ def render_dns_dynamic_update_interface_static_ip_address(op):
534 SELECT host(ip) INTO ip_addr FROM maasserver_staticipaddress WHERE id=NEW.staticipaddress_id;
535 IF (node_type={NODE_TYPE.MACHINE} OR node_type={NODE_TYPE.DEVICE}) THEN
536 IF (iface_id = boot_iface_id OR boot_iface_id is NULL) THEN
537- PERFORM pg_notify('sys_dns_updates', 'INSERT ' || domain || ' ' || current_hostname || ' A ' || address_ttl || ' ' || ip_addr);
538+ PERFORM pg_notify('sys_dns_updates', insert_boot_interface_dns_notification(domain, current_hostname, address_ttl, ip_addr));
539 ELSE
540- PERFORM pg_notify('sys_dns_updates', 'INSERT ' || domain || ' ' || iface_name || '.' || current_hostname || ' A ' || address_ttl || ' ' || ip_addr);
541+ PERFORM pg_notify('sys_dns_updates', insert_non_boot_interface_dns_notification(domain, iface_name, current_hostname, address_ttl, ip_addr));
542 END IF;
543 END IF;
544 ELSIF (TG_OP = 'DELETE' AND TG_LEVEL = 'ROW') THEN
545@@ -2126,13 +2331,13 @@ def render_dns_dynamic_update_interface_static_ip_address(op):
546 IF EXISTS(SELECT id FROM maasserver_staticipaddress WHERE id=OLD.staticipaddress_id) THEN
547 SELECT host(ip) INTO ip_addr FROM maasserver_staticipaddress WHERE id=OLD.staticipaddress_id;
548 IF (iface_id = boot_iface_id) THEN
549- PERFORM pg_notify('sys_dns_updates', 'DELETE ' || domain || ' ' || current_hostname || ' A ' || ip_addr);
550+ PERFORM pg_notify('sys_dns_updates', delete_boot_interface_dns_notification(domain, current_hostname, ip_addr));
551 ELSE
552- PERFORM pg_notify('sys_dns_updates', 'DELETE ' || domain || ' ' || iface_name || '.' || current_hostname || ' A ' || ip_addr);
553+ PERFORM pg_notify('sys_dns_updates', delete_non_boot_interface_dns_notification(domain, iface_name, current_hostname, ip_addr));
554 END IF;
555 ELSE
556- PERFORM pg_notify('sys_dns_updates', 'DELETE-IFACE-IP ' || domain || ' ' || current_hostname || ' A ' || OLD.interface_id);
557- PERFORM pg_notify('sys_dns_updates', 'DELETE-IFACE-IP ' || domain || ' ' || current_hostname || ' AAAA ' || OLD.interface_id);
558+ PERFORM pg_notify('sys_dns_updates', delete_iface_ip_dns_notification(domain, current_hostname, 'A', OLD.interface_id));
559+ PERFORM pg_notify('sys_dns_updates', delete_iface_ip_dns_notification(domain, current_hostname, 'AAAA', OLD.interface_id));
560 END IF;
561 END IF;
562 END IF;
563@@ -2154,20 +2359,41 @@ dns_dynamic_update_static_ip_address_update = dedent(
564 iface_name text;
565 address_ttl int;
566 current_interface_id bigint;
567+ iface_id bigint;
568+ boot_iface_id bigint;
569 BEGIN
570 IF NEW IS DISTINCT FROM OLD THEN
571 IF EXISTS(SELECT id FROM maasserver_interface_ip_addresses WHERE staticipaddress_id=NEW.id) THEN
572 SELECT interface_id INTO current_interface_id FROM maasserver_interface_ip_addresses WHERE staticipaddress_id=NEW.id;
573- SELECT iface.name, node.hostname, domain_tbl.name, COALESCE(domain_tbl.ttl, 0) INTO iface_name, current_hostname, domain, address_ttl
574- FROM maasserver_interface AS iface
575+ SELECT
576+ iface.name,
577+ node.hostname,
578+ domain_tbl.name,
579+ COALESCE(domain_tbl.ttl, 0),
580+ iface.id,
581+ node.boot_interface_id
582+ INTO
583+ iface_name,
584+ current_hostname,
585+ domain,
586+ address_ttl,
587+ iface_id,
588+ boot_iface_id
589+ FROM maasserver_interface AS iface
590 JOIN maasserver_node AS node ON iface.node_config_id = node.current_config_id
591 JOIN maasserver_domain AS domain_tbl ON domain_tbl.id=node.domain_id WHERE iface.id=current_interface_id;
592 IF OLD.ip IS NOT NULL THEN
593- PERFORM pg_notify('sys_dns_updates', 'DELETE ' || domain || ' ' || current_hostname || ' A ' || host(OLD.ip));
594- PERFORM pg_notify('sys_dns_updates', 'DELETE ' || domain || ' ' || iface_name || '.' || current_hostname || ' A ' || host(OLD.ip));
595+ IF (iface_id = boot_iface_id OR boot_iface_id is NULL) THEN
596+ PERFORM pg_notify('sys_dns_updates', delete_boot_interface_dns_notification(domain, current_hostname, host(OLD.ip)));
597+ ELSE
598+ PERFORM pg_notify('sys_dns_updates', delete_non_boot_interface_dns_notification(domain, iface_name, current_hostname, host(OLD.ip)));
599+ END IF;
600+ END IF;
601+ IF (iface_id = boot_iface_id OR boot_iface_id is NULL) THEN
602+ PERFORM pg_notify('sys_dns_updates', insert_boot_interface_dns_notification(domain, current_hostname, address_ttl, host(NEW.ip)));
603+ ELSE
604+ PERFORM pg_notify('sys_dns_updates', insert_non_boot_interface_dns_notification(domain, iface_name, current_hostname, address_ttl, host(NEW.ip)));
605 END IF;
606- PERFORM pg_notify('sys_dns_updates', 'INSERT ' || domain || ' ' || current_hostname || ' A ' || address_ttl || ' ' || host(NEW.ip));
607- PERFORM pg_notify('sys_dns_updates', 'INSERT ' || domain || ' ' || iface_name || '.' || current_hostname || ' A ' || address_ttl || ' ' || host(NEW.ip));
608 END IF;
609 END IF;
610 RETURN NULL;
611@@ -2191,7 +2417,7 @@ def render_dns_dynamic_update_node(op):
612 new_ip text;
613 BEGIN
614 IF NEW.node_type <> {NODE_TYPE.DEVICE} AND NEW.node_type <> {NODE_TYPE.MACHINE} THEN
615- PERFORM pg_notify('sys_dns_updates', 'RELOAD');
616+ PERFORM pg_notify('sys_dns_updates', reload_dns_notification());
617 ELSE
618 IF (TG_OP = 'UPDATE' AND TG_LEVEL = 'ROW') THEN
619 SELECT name, COALESCE(ttl, 0) INTO domain, address_ttl FROM maasserver_domain WHERE id=NEW.domain_id;
620@@ -2201,21 +2427,21 @@ def render_dns_dynamic_update_node(op):
621 FROM maasserver_interface_ip_addresses AS link
622 JOIN maasserver_interface AS iface ON link.interface_id = iface.id
623 JOIN maasserver_staticipaddress AS ip_addr ON link.staticipaddress_id = ip_addr.id WHERE link.interface_id = OLD.boot_interface_id AND ip_addr.ip IS NOT NULL;
624- PERFORM pg_notify('sys_dns_updates', 'DELETE ' || domain || ' ' || OLD.hostname || ' A ' || old_ip);
625- PERFORM pg_notify('sys_dns_updates', 'INSERT ' || domain || ' ' || old_iface_name || '.' || NEW.hostname || ' A ' || address_ttl || ' ' || old_ip);
626+ PERFORM pg_notify('sys_dns_updates', delete_boot_interface_dns_notification(domain, OLD.hostname, old_ip));
627+ PERFORM pg_notify('sys_dns_updates', insert_non_boot_interface_dns_notification(domain, old_iface_name, NEW.hostname, address_ttl, old_ip));
628 END IF;
629 SELECT iface.name, host(ip_addr.ip) INTO iface_name, new_ip
630 FROM maasserver_interface_ip_addresses AS link
631 JOIN maasserver_interface AS iface ON link.interface_id = iface.id
632 JOIN maasserver_staticipaddress AS ip_addr on link.staticipaddress_id = ip_addr.id WHERE link.interface_id = NEW.boot_interface_id AND ip_addr.ip IS NOT NULL;
633- PERFORM pg_notify('sys_dns_updates', 'DELETE ' || domain || ' ' || iface_name || '.' || OLD.hostname || ' A ' || new_ip);
634- PERFORM pg_notify('sys_dns_updates', 'INSERT ' || domain || ' ' || NEW.hostname || ' A ' || address_ttl || ' ' || new_ip);
635+ PERFORM pg_notify('sys_dns_updates', delete_non_boot_interface_dns_notification(domain, iface_name, OLD.hostname, new_ip));
636+ PERFORM pg_notify('sys_dns_updates', insert_boot_interface_dns_notification(domain, NEW.hostname, address_ttl, new_ip));
637 ELSIF (OLD.hostname <> NEW.hostname) THEN
638- PERFORM pg_notify('sys_dns_updates', 'RELOAD');
639+ PERFORM pg_notify('sys_dns_updates', reload_dns_notification());
640 END IF;
641 ELSE
642 SELECT name, COALESCE(ttl, 0) INTO domain, address_ttl FROM maasserver_domain WHERE id=OLD.domain_id;
643- PERFORM pg_notify('sys_dns_updates', 'DELETE ' || domain || ' ' || OLD.hostname || ' A');
644+ PERFORM pg_notify('sys_dns_updates', delete_boot_interface_dns_notification(domain, OLD.hostname, ''));
645 END IF;
646 END IF;
647 RETURN NULL;
648@@ -2239,7 +2465,7 @@ dns_dynamic_update_interface_delete = dedent(
649 SELECT node_id INTO current_node_id FROM maasserver_nodeconfig WHERE id=OLD.node_config_id;
650 SELECT hostname, domain_id INTO current_hostname, current_domain_id FROM maasserver_node WHERE id=current_node_id;
651 SELECT name INTO domain FROM maasserver_domain WHERE id=current_domain_id;
652- PERFORM pg_notify('sys_dns_updates', 'DELETE ' || domain || ' ' || OLD.name || '.' || current_hostname || ' A');
653+ PERFORM pg_notify('sys_dns_updates', delete_non_boot_interface_dns_notification(domain, OLD.name, current_hostname, ''));
654 END IF;
655 RETURN NULL;
656 END;
657@@ -2277,6 +2503,18 @@ def register_system_triggers():
658 register_procedure(CORE_GET_NUMBER_OF_PROCESSES)
659 register_procedure(CORE_PICK_NEW_REGION)
660 register_procedure(CORE_SET_NEW_REGION)
661+ register_procedure(CORE_GEN_RANDOM_PREFIX)
662+ register_procedure(CORE_UPDATE_DATA_DNS_NOTIFICATION_FORMAT)
663+ register_procedure(CORE_INSERT_DATA_DNS_NOTIFICATION_FORMAT)
664+ register_procedure(CORE_DELETE_IP_DNS_NOTIFICATION_FORMAT)
665+ register_procedure(CORE_DELETE_IFACE_IP_DNS_NOTIFICATION_FORMAT)
666+ register_procedure(CORE_BOOT_INTERFACE_INSERT_DNS_NOTIFICATION_FORMAT)
667+ register_procedure(CORE_NON_BOOT_INTERFACE_INSERT_DNS_NOTIFICATION_FORMAT)
668+ register_procedure(CORE_BOOT_INTERFACE_DELETE_DNS_NOTIFICATION_FORMAT)
669+ register_procedure(CORE_DELETE_DNS_NOTIFICATION_FORMAT)
670+ register_procedure(CORE_NON_BOOT_INTERFACE_DELETE_DNS_NOTIFICATION_FORMAT)
671+ register_procedure(CORE_BOOT_INTERFACE_UPDATE_DNS_NOTIFICATION_FORMAT)
672+ register_procedure(CORE_RELOAD_DNS_NOTIFICATION_FORMAT)
673
674 # RegionRackRPCConnection
675 register_procedure(CORE_REGIONRACKRPCONNECTION_INSERT)
676diff --git a/src/maasserver/triggers/tests/test_system.py b/src/maasserver/triggers/tests/test_system.py
677index 417bf6e..d236205 100644
678--- a/src/maasserver/triggers/tests/test_system.py
679+++ b/src/maasserver/triggers/tests/test_system.py
680@@ -15,6 +15,9 @@ from maasserver.testing.testcase import (
681 MAASServerTestCase,
682 MAASTransactionServerTestCase,
683 )
684+from maasserver.triggers.models.dns_notifications import (
685+ DynamicDNSUpdateNotification,
686+)
687 from maasserver.triggers.system import register_system_triggers
688 from maasserver.triggers.testing import (
689 NotifyHelperMixin,
690@@ -132,8 +135,11 @@ class TestSysDNSUpdates(
691 "sys_dns_updates"
692 ) # ignore RELOAD from domain creation
693 msg = yield self.get_notify("sys_dns_updates")
694+ decoded_msg = DynamicDNSUpdateNotification(
695+ msg
696+ ).get_decoded_message()
697 self.assertEqual(
698- msg,
699+ decoded_msg,
700 f"INSERT {domain.name} {rec.name} A {domain.ttl if domain.ttl else 0} {static_ip.ip}",
701 )
702 finally:
703@@ -162,8 +168,12 @@ class TestSysDNSUpdates(
704 try:
705 yield deferToDatabase(rec.delete)
706 msg = yield self.get_notify("sys_dns_updates")
707+ decoded_msg = DynamicDNSUpdateNotification(
708+ msg
709+ ).get_decoded_message()
710 self.assertEqual(
711- msg, f"DELETE {domain.name} {rec.name} A {static_ip.ip}"
712+ decoded_msg,
713+ f"DELETE {domain.name} {rec.name} A {static_ip.ip}",
714 )
715 finally:
716 self.stop_reading()
717@@ -191,8 +201,12 @@ class TestSysDNSUpdates(
718 rec.address_ttl = 30
719 yield deferToDatabase(rec.save)
720 msg = yield self.get_notify("sys_dns_updates")
721+ decoded_msg = DynamicDNSUpdateNotification(
722+ msg
723+ ).get_decoded_message()
724 self.assertEqual(
725- msg, f"UPDATE {domain.name} {rec.name} A 30 {static_ip.ip}"
726+ decoded_msg,
727+ f"UPDATE {domain.name} {rec.name} A 30 {static_ip.ip}",
728 )
729 finally:
730 self.stop_reading()
731@@ -219,8 +233,12 @@ class TestSysDNSUpdates(
732 try:
733 yield deferToDatabase(rec.delete)
734 msg = yield self.get_notify("sys_dns_updates")
735+ decoded_msg = DynamicDNSUpdateNotification(
736+ msg
737+ ).get_decoded_message()
738 self.assertEqual(
739- msg, f"DELETE {domain.name} {rec.name} A {static_ip.ip}"
740+ decoded_msg,
741+ f"DELETE {domain.name} {rec.name} A {static_ip.ip}",
742 )
743 finally:
744 self.stop_reading()
745@@ -254,8 +272,11 @@ class TestSysDNSUpdates(
746 dnsdata.rrdata = factory.make_name()
747 yield deferToDatabase(dnsdata.save)
748 msg = yield self.get_notify("sys_dns_updates")
749+ decoded_msg = DynamicDNSUpdateNotification(
750+ msg
751+ ).get_decoded_message()
752 self.assertEqual(
753- msg,
754+ decoded_msg,
755 f"UPDATE-DATA {dnsdata.id}",
756 )
757 finally:
758@@ -288,8 +309,11 @@ class TestSysDNSUpdates(
759 },
760 )
761 msg = yield self.get_notify("sys_dns_updates")
762+ decoded_msg = DynamicDNSUpdateNotification(
763+ msg
764+ ).get_decoded_message()
765 self.assertEqual(
766- msg,
767+ decoded_msg,
768 f"INSERT-DATA {dnsdata.id}",
769 )
770 finally:
771@@ -323,8 +347,12 @@ class TestSysDNSUpdates(
772 try:
773 yield deferToDatabase(dnsdata.delete)
774 msg = yield self.get_notify("sys_dns_updates")
775+ decoded_msg = DynamicDNSUpdateNotification(
776+ msg
777+ ).get_decoded_message()
778 self.assertEqual(
779- msg, f"DELETE {domain.name} {rec.name} {dnsdata.rrtype}"
780+ decoded_msg,
781+ f"DELETE {domain.name} {rec.name} {dnsdata.rrtype}",
782 )
783 finally:
784 self.stop_reading()
785@@ -345,7 +373,10 @@ class TestSysDNSUpdates(
786 try:
787 yield deferToDatabase(self.create_domain)
788 msg = yield self.get_notify("sys_dns_updates")
789- self.assertEqual(msg, "RELOAD")
790+ decoded_msg = DynamicDNSUpdateNotification(
791+ msg
792+ ).get_decoded_message()
793+ self.assertEqual(decoded_msg, "RELOAD")
794 finally:
795 self.stop_reading()
796 yield self.postgres_listener_service.stopService()
797@@ -365,7 +396,10 @@ class TestSysDNSUpdates(
798 try:
799 yield deferToDatabase(self.create_subnet)
800 msg = yield self.get_notify("sys_dns_updates")
801- self.assertEqual(msg, "RELOAD")
802+ decoded_msg = DynamicDNSUpdateNotification(
803+ msg
804+ ).get_decoded_message()
805+ self.assertEqual(decoded_msg, "RELOAD")
806 finally:
807 self.stop_reading()
808 yield self.postgres_listener_service.stopService()
809@@ -415,8 +449,11 @@ class TestSysDNSUpdates(
810 )
811 )
812 msg1 = yield self.get_notify("sys_dns_updates")
813+ decoded_msg1 = DynamicDNSUpdateNotification(
814+ msg1
815+ ).get_decoded_message()
816 self.assertEqual(
817- msg1,
818+ decoded_msg1,
819 f"INSERT {domain.name} {node.hostname} A 0 {expected_ip.ip}",
820 )
821 finally:
822@@ -471,8 +508,11 @@ class TestSysDNSUpdates(
823 )
824 )
825 msg1 = yield self.get_notify("sys_dns_updates")
826+ decoded_msg1 = DynamicDNSUpdateNotification(
827+ msg1
828+ ).get_decoded_message()
829 self.assertEqual(
830- msg1,
831+ decoded_msg1,
832 f"INSERT {domain.name} {node.hostname} A 0 {expected_ip.ip}",
833 )
834 finally:
835@@ -516,8 +556,12 @@ class TestSysDNSUpdates(
836 try:
837 yield deferToDatabase(iface.unlink_ip_address, ip1)
838 msg1 = yield self.get_notify("sys_dns_updates")
839+ decoded_msg1 = DynamicDNSUpdateNotification(
840+ msg1
841+ ).get_decoded_message()
842 self.assertEqual(
843- msg1, f"DELETE {domain.name} {node.hostname} A {ip1.ip}"
844+ decoded_msg1,
845+ f"DELETE {domain.name} {node.hostname} A {ip1.ip}",
846 )
847 finally:
848 self.stop_reading()
849@@ -590,14 +634,17 @@ class TestSysDNSUpdates(
850 )
851 for exp in expected_msgs:
852 msg = yield self.get_notify("sys_dns_updates")
853- self.assertEqual(msg, exp)
854+ decoded_msg = DynamicDNSUpdateNotification(
855+ msg
856+ ).get_decoded_message()
857+ self.assertEqual(decoded_msg, exp)
858 finally:
859 self.stop_reading()
860 yield self.postgres_listener_service.stopService()
861
862 @wait_for_reactor
863 @inlineCallbacks
864- def test_dns_dynamc_update_ip_update(self):
865+ def test_dns_dynamc_update_ip_boot_interface_update(self):
866 listener = self.make_listener_without_delay()
867 yield self.set_service(listener)
868 yield deferToDatabase(
869@@ -638,22 +685,87 @@ class TestSysDNSUpdates(
870 try:
871 yield deferToDatabase(_set_new_ip)
872 msg1 = yield self.get_notify("sys_dns_updates")
873+ decoded_msg1 = DynamicDNSUpdateNotification(
874+ msg1
875+ ).get_decoded_message()
876 self.assertEqual(
877- msg1, f"DELETE {domain.name} {node.hostname} A {old_ip}"
878+ decoded_msg1,
879+ f"DELETE {domain.name} {node.hostname} A {old_ip}",
880 )
881 msg2 = yield self.get_notify("sys_dns_updates")
882+ decoded_msg2 = DynamicDNSUpdateNotification(
883+ msg2
884+ ).get_decoded_message()
885 self.assertEqual(
886- msg2,
887- f"DELETE {domain.name} {iface.name}.{node.hostname} A {old_ip}",
888+ decoded_msg2,
889+ f"INSERT {domain.name} {node.hostname} A 0 {ip.ip}",
890 )
891- msg3 = yield self.get_notify("sys_dns_updates")
892+ finally:
893+ self.stop_reading()
894+ yield self.postgres_listener_service.stopService()
895+
896+ @wait_for_reactor
897+ @inlineCallbacks
898+ def test_dns_dynamc_update_ip_non_boot_interface_update(self):
899+ listener = self.make_listener_without_delay()
900+ yield self.set_service(listener)
901+ yield deferToDatabase(
902+ self.register_trigger,
903+ "maasserver_staticipaddress",
904+ "sys_dns_updates",
905+ ops=("update",),
906+ trigger="sys_dns_updates_ip_update",
907+ )
908+ vlan = yield deferToDatabase(self.create_vlan)
909+ subnet = yield deferToDatabase(
910+ self.create_subnet, params={"vlan": vlan}
911+ )
912+ node = yield deferToDatabase(
913+ self.create_node_with_interface,
914+ params={"subnet": subnet, "status": NODE_STATUS.DEPLOYED},
915+ )
916+ domain = yield deferToDatabase(Domain.objects.get_default_domain)
917+ # set boot interface
918+ yield deferToDatabase(
919+ lambda: node.current_config.interface_set.first()
920+ )
921+ iface2 = yield deferToDatabase(
922+ self.create_interface, params={"node": node}
923+ )
924+ # Change ip of the non-boot interface
925+ ip = yield deferToDatabase(
926+ lambda: self.create_staticipaddress(
927+ params={
928+ "ip": subnet.get_next_ip_for_allocation()[0],
929+ "interface": iface2,
930+ "subnet": subnet,
931+ }
932+ )
933+ )
934+ old_ip = ip.ip
935+
936+ def _set_new_ip():
937+ ip.ip = subnet.get_next_ip_for_allocation()[0]
938+ ip.save()
939+
940+ self.start_reading()
941+ try:
942+ yield deferToDatabase(_set_new_ip)
943+ msg1 = yield self.get_notify("sys_dns_updates")
944+ decoded_msg1 = DynamicDNSUpdateNotification(
945+ msg1
946+ ).get_decoded_message()
947 self.assertEqual(
948- msg3, f"INSERT {domain.name} {node.hostname} A 0 {ip.ip}"
949+ decoded_msg1,
950+ f"DELETE {domain.name} {iface2.name}.{node.hostname} A {old_ip}",
951 )
952- msg4 = yield self.get_notify("sys_dns_updates")
953+ msg2 = yield self.get_notify("sys_dns_updates")
954+ decoded_msg2 = DynamicDNSUpdateNotification(
955+ msg2
956+ ).get_decoded_message()
957 self.assertEqual(
958- msg4,
959- f"INSERT {domain.name} {iface.name}.{node.hostname} A 0 {ip.ip}",
960+ decoded_msg2,
961+ f"INSERT {domain.name} {iface2.name}.{node.hostname} A 0 {ip.ip}",
962 )
963 finally:
964 self.stop_reading()
965@@ -674,7 +786,10 @@ class TestSysDNSUpdates(
966 try:
967 yield deferToDatabase(self.create_rack_controller)
968 msg = yield self.get_notify("sys_dns_updates")
969- self.assertEqual(msg, "RELOAD")
970+ decoded_msg = DynamicDNSUpdateNotification(
971+ msg
972+ ).get_decoded_message()
973+ self.assertEqual(decoded_msg, "RELOAD")
974 finally:
975 self.stop_reading()
976 yield self.postgres_listener_service.stopService()
977@@ -696,7 +811,10 @@ class TestSysDNSUpdates(
978 controller.cpu_speed = 10
979 yield deferToDatabase(controller.save)
980 msg = yield self.get_notify("sys_dns_updates")
981- self.assertEqual(msg, "RELOAD")
982+ decoded_msg = DynamicDNSUpdateNotification(
983+ msg
984+ ).get_decoded_message()
985+ self.assertEqual(decoded_msg, "RELOAD")
986 finally:
987 self.stop_reading()
988 yield self.postgres_listener_service.stopService()
989@@ -718,14 +836,19 @@ class TestSysDNSUpdates(
990 try:
991 yield deferToDatabase(node.delete)
992 msg = yield self.get_notify("sys_dns_updates")
993- self.assertEqual(msg, f"DELETE {domain.name} {node.hostname} A")
994+ decoded_msg = DynamicDNSUpdateNotification(
995+ msg
996+ ).get_decoded_message()
997+ self.assertEqual(
998+ decoded_msg, f"DELETE {domain.name} {node.hostname} A"
999+ )
1000 finally:
1001 self.stop_reading()
1002 yield self.postgres_listener_service.stopService()
1003
1004 @wait_for_reactor
1005 @inlineCallbacks
1006- def test_dns_dynamic_update_interface_delete(self):
1007+ def test_dns_dynamic_update_boot_interface_delete(self):
1008 listener = self.make_listener_without_delay()
1009 yield self.set_service(listener)
1010 yield deferToDatabase(
1011@@ -745,9 +868,57 @@ class TestSysDNSUpdates(
1012 self.start_reading()
1013 try:
1014 yield deferToDatabase(iface.delete)
1015- msg = yield self.get_notify("sys_dns_updates")
1016+ msg1 = yield self.get_notify("sys_dns_updates")
1017+ decoded_msg = DynamicDNSUpdateNotification(
1018+ msg1
1019+ ).get_decoded_message()
1020+ self.assertEqual(
1021+ decoded_msg, f"DELETE {domain.name} {node.hostname} A"
1022+ )
1023+ msg2 = yield self.get_notify("sys_dns_updates")
1024+ decoded_msg2 = DynamicDNSUpdateNotification(
1025+ msg2
1026+ ).get_decoded_message()
1027+ self.assertEqual(
1028+ decoded_msg2,
1029+ f"DELETE {domain.name} {iface.name}.{node.hostname} A",
1030+ )
1031+ finally:
1032+ self.stop_reading()
1033+ yield self.postgres_listener_service.stopService()
1034+
1035+ @wait_for_reactor
1036+ @inlineCallbacks
1037+ def test_dns_dynamic_update_non_boot_interface_delete(self):
1038+ listener = self.make_listener_without_delay()
1039+ yield self.set_service(listener)
1040+ yield deferToDatabase(
1041+ self.register_trigger,
1042+ "maasserver_node",
1043+ "sys_dns_updates",
1044+ ops=("delete",),
1045+ )
1046+ subnet = yield deferToDatabase(self.create_subnet)
1047+ node = yield deferToDatabase(
1048+ self.create_node_with_interface, params={"subnet": subnet}
1049+ )
1050+ domain = yield deferToDatabase(Domain.objects.get_default_domain)
1051+ yield deferToDatabase(
1052+ lambda: node.current_config.interface_set.first()
1053+ )
1054+ iface2 = yield deferToDatabase(
1055+ self.create_interface, params={"node": node}
1056+ )
1057+ self.start_reading()
1058+ try:
1059+ yield deferToDatabase(iface2.delete)
1060+ msg1 = yield self.get_notify("sys_dns_updates")
1061+ decoded_msg = DynamicDNSUpdateNotification(
1062+ msg1
1063+ ).get_decoded_message()
1064 self.assertEqual(
1065- msg, f"DELETE {domain.name} {iface.name}.{node.hostname} A"
1066+ decoded_msg,
1067+ f"DELETE {domain.name} {iface2.name}.{node.hostname} A",
1068 )
1069 finally:
1070 self.stop_reading()
1071@@ -812,12 +983,19 @@ class TestSysDNSUpdates(
1072 )
1073 )
1074 msg1 = yield self.get_notify("sys_dns_updates")
1075+ decoded_msg1 = DynamicDNSUpdateNotification(
1076+ msg1
1077+ ).get_decoded_message()
1078 self.assertEqual(
1079- msg1, f"INSERT {domain.name} {node.hostname} A 0 {ip1.ip}"
1080+ decoded_msg1,
1081+ f"INSERT {domain.name} {node.hostname} A 0 {ip1.ip}",
1082 )
1083 msg2 = yield self.get_notify("sys_dns_updates")
1084+ decoded_msg2 = DynamicDNSUpdateNotification(
1085+ msg2
1086+ ).get_decoded_message()
1087 self.assertEqual(
1088- msg2,
1089+ decoded_msg2,
1090 f"INSERT {domain.name} {iface2.name}.{node.hostname} A 0 {ip2.ip}",
1091 )
1092 finally:
1093@@ -873,8 +1051,12 @@ class TestSysDNSUpdates(
1094 )
1095 )
1096 msg1 = yield self.get_notify("sys_dns_updates")
1097+ decoded_msg1 = DynamicDNSUpdateNotification(
1098+ msg1
1099+ ).get_decoded_message()
1100 self.assertEqual(
1101- msg1, f"INSERT {domain.name} {node.hostname} A 0 {ip1.ip}"
1102+ decoded_msg1,
1103+ f"INSERT {domain.name} {node.hostname} A 0 {ip1.ip}",
1104 )
1105 finally:
1106 self.stop_reading()
1107@@ -943,23 +1125,35 @@ class TestSysDNSUpdates(
1108 node.boot_interface = iface2
1109 yield deferToDatabase(node.save)
1110 msg1 = yield self.get_notify("sys_dns_updates")
1111+ decoded_msg1 = DynamicDNSUpdateNotification(
1112+ msg1
1113+ ).get_decoded_message()
1114 self.assertEqual(
1115- msg1,
1116+ decoded_msg1,
1117 f"DELETE {domain.name} {node.hostname} A {ip1.ip}",
1118 )
1119 msg2 = yield self.get_notify("sys_dns_updates")
1120+ decoded_msg2 = DynamicDNSUpdateNotification(
1121+ msg2
1122+ ).get_decoded_message()
1123 self.assertEqual(
1124- msg2,
1125+ decoded_msg2,
1126 f"INSERT {domain.name} {iface1.name}.{node.hostname} A 0 {ip1.ip}",
1127 )
1128 msg3 = yield self.get_notify("sys_dns_updates")
1129+ decoded_msg3 = DynamicDNSUpdateNotification(
1130+ msg3
1131+ ).get_decoded_message()
1132 self.assertEqual(
1133- msg3,
1134+ decoded_msg3,
1135 f"DELETE {domain.name} {iface2.name}.{node.hostname} A {ip2.ip}",
1136 )
1137 msg4 = yield self.get_notify("sys_dns_updates")
1138+ decoded_msg4 = DynamicDNSUpdateNotification(
1139+ msg4
1140+ ).get_decoded_message()
1141 self.assertEqual(
1142- msg4,
1143+ decoded_msg4,
1144 f"INSERT {domain.name} {node.hostname} A 0 {ip2.ip}",
1145 )
1146 finally:

Subscribers

People subscribed via source and target branches