diff --git a/designate/central/service.py b/designate/central/service.py index ac97545a..9092a20e 100644 --- a/designate/central/service.py +++ b/designate/central/service.py @@ -2265,6 +2265,8 @@ class Service(service.RPCService, service.Service): return pool # Pool Manager Integration + @notification('dns.domain.update') + @notification('dns.zone.update') @transaction def update_status(self, context, zone_id, status, serial): """ @@ -2272,14 +2274,19 @@ class Service(service.RPCService, service.Service): :param zone_id: The ID of the designate zone. :param status: The status, 'SUCCESS' or 'ERROR'. :param serial: The consensus serial number for the zone. - :return: None + :return: updated zone """ # TODO(kiall): If the status is SUCCESS and the zone is already ACTIVE, # we likely don't need to do anything. self._update_record_status(context, zone_id, status, serial) - self._update_zone_status(context, zone_id, status, serial) + zone = self._update_zone_status(context, zone_id, status, serial) + return zone def _update_zone_status(self, context, zone_id, status, serial): + """Update zone status in storage + + :return: updated zone + """ zone = self.storage.get_zone(context, zone_id) zone, deleted = self._update_zone_or_record_status( @@ -2298,7 +2305,12 @@ class Service(service.RPCService, service.Service): # that the action, status and serial are updated correctly. self.storage.delete_zone(context, zone.id) + return zone + def _update_record_status(self, context, zone_id, status, serial): + """Update status on every record in a zone based on `serial` + :returns: updated records + """ criterion = { 'zone_id': zone_id } @@ -2347,6 +2359,8 @@ class Service(service.RPCService, service.Service): if len(recordset.records) == 0: self.storage.delete_recordset(context, recordset.id) + return records + @staticmethod def _update_zone_or_record_status(zone_or_record, status, serial): deleted = False diff --git a/designate/mdns/notify.py b/designate/mdns/notify.py index 8f92e6a8..174121cd 100644 --- a/designate/mdns/notify.py +++ b/designate/mdns/notify.py @@ -66,7 +66,9 @@ class NotifyEndpoint(base.BaseEndpoint): def poll_for_serial_number(self, context, zone, nameserver, timeout, retry_interval, max_retries, delay): - """ + """Get the serial number of a zone on a resolver, then call update_status + on Pool Manager to update the zone status. + :param context: The user context. :param zone: The designate zone object. This contains the zone name. zone.serial = expected_serial @@ -78,7 +80,7 @@ class NotifyEndpoint(base.BaseEndpoint): an expected serial number. After this many retries, mindns returns an ERROR. :param delay: The time to wait before sending the first request. - :return: The pool manager is informed of the status with update_status. + :return: None """ (status, actual_serial, retries) = self.get_serial_number( context, zone, nameserver.host, nameserver.port, timeout, diff --git a/designate/pool_manager/service.py b/designate/pool_manager/service.py index abfedba3..0095162e 100644 --- a/designate/pool_manager/service.py +++ b/designate/pool_manager/service.py @@ -247,7 +247,11 @@ class Service(service.RPCService, coordination.CoordinationMixin, # Standard Create/Update/Delete Methods def create_zone(self, context, zone): - """ + """Called by Central or by periodic_recovery, instruct the backends to + create a zone, then poll for consensus. + On success, send NOTIFY to also_notifies and nameservers + Finally, poll for zone serial number on nameservers. + :param context: Security context information. :param zone: Zone to be created :return: None @@ -291,7 +295,8 @@ class Service(service.RPCService, coordination.CoordinationMixin, self.retry_interval, self.max_retries, self.delay) def _create_zone_on_target(self, context, target, zone): - """ + """Called by create_zone, run create_zone on backends + :param context: Security context information. :param target: Target to create Zone on :param zone: Zone to be created @@ -354,8 +359,8 @@ class Service(service.RPCService, coordination.CoordinationMixin, for nameserver in self.pool.nameservers: # See if there is already another update in progress try: - update_status = self.cache.retrieve( - context, nameserver.id, zone.id, UPDATE_ACTION) + self.cache.retrieve(context, nameserver.id, zone.id, + UPDATE_ACTION) except exceptions.PoolManagerStatusNotFound: update_status = self._build_status_object( nameserver, zone, UPDATE_ACTION) diff --git a/designate/tests/test_central/test_service.py b/designate/tests/test_central/test_service.py index 6d6a5558..22783bc3 100644 --- a/designate/tests/test_central/test_service.py +++ b/designate/tests/test_central/test_service.py @@ -3006,6 +3006,41 @@ class CentralServiceTest(CentralTestCase): self.admin_context, zone['id'], recordset['id'], record['id']) + @mock.patch.object(notifier.Notifier, "info") + def test_update_status_send_notification(self, mock_notifier): + + # Create zone and ensure that two zone/domain create notifications + # have been sent - status is PENDING + zone = self.create_zone(email='info@example.org') + self.assertEqual(2, mock_notifier.call_count) + + notify_string, notified_zone = mock_notifier.call_args_list[0][0][1:] + self.assertEqual('dns.domain.create', notify_string) + self.assertEqual('example.com.', notified_zone.name) + self.assertEqual('PENDING', notified_zone.status) + + notify_string, notified_zone = mock_notifier.call_args_list[1][0][1:] + self.assertEqual('dns.zone.create', notify_string) + self.assertEqual('example.com.', notified_zone.name) + self.assertEqual('PENDING', notified_zone.status) + + # Perform an update; ensure that zone/domain update notifications + # have been sent and the zone is now in ACTIVE status + mock_notifier.reset_mock() + self.central_service.update_status( + self.admin_context, zone['id'], "SUCCESS", zone.serial) + + self.assertEqual(2, mock_notifier.call_count) + notify_string, notified_zone = mock_notifier.call_args_list[0][0][1:] + self.assertEqual('dns.domain.update', notify_string) + self.assertEqual('example.com.', notified_zone.name) + self.assertEqual('ACTIVE', notified_zone.status) + + notify_string, notified_zone = mock_notifier.call_args_list[1][0][1:] + self.assertEqual('dns.zone.update', notify_string) + self.assertEqual('example.com.', notified_zone.name) + self.assertEqual('ACTIVE', notified_zone.status) + def test_update_status_delete_last_record_without_incrementing_serial( self): zone = self.create_zone()