Set zone in ERROR status on periodic sync fail

Improve testing

Change-Id: I1d65ed13fc185858ffe477e4bca6303e01a12c1d
Closes-Bug: #1416263
This commit is contained in:
Federico Ceratto 2015-12-09 15:45:57 +00:00
parent 4b240b910d
commit 9d2027f788
3 changed files with 169 additions and 5 deletions

View File

@ -221,11 +221,16 @@ class Service(service.RPCService, service.Service):
# TODO(kiall): If the domain was created within the last
# periodic_sync_seconds, attempt to recreate
# to fill in targets which may have failed.
self.update_domain(context, domain)
success = self.update_domain(context, domain)
if not success:
self.central_api.update_status(
context, domain.id, ERROR_STATUS, domain.serial)
except Exception:
LOG.exception(_LE('An unhandled exception in periodic '
'synchronization occurred.'))
self.central_api.update_status(context, domain.id,
ERROR_STATUS, domain.serial)
# Standard Create/Update/Delete Methods
def create_domain(self, context, domain):
@ -316,7 +321,7 @@ class Service(service.RPCService, service.Service):
self.central_api.update_status(
context, domain.id, ERROR_STATUS, domain.serial)
return
return False
# Send a NOTIFY to each also-notifies
for also_notify in self.pool.also_notifies:
@ -335,6 +340,8 @@ class Service(service.RPCService, service.Service):
self._update_domain_on_nameserver(context, nameserver, domain)
return True
def _update_domain_on_target(self, context, target, domain):
"""
:param context: Security context information.

View File

@ -13,9 +13,14 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import uuid
import fixtures
from mock import call
from oslo import messaging
from oslo.config import cfg
from mock import call
from oslo_concurrency import lockutils
from mock import Mock
from mock import patch
from designate import exceptions
@ -100,10 +105,18 @@ class PoolManagerServiceNoopTest(PoolManagerTestCase):
self.service = self.start_service('pool_manager')
self.cache = self.service.cache
self.tmpdir = self.useFixture(fixtures.TempDir()).path
cfg.CONF.oslo_concurrency.lock_path = self.tmpdir
def test_oslo_concurrency_loc_path(self):
path = lockutils._get_lock_path('a', 'b')
self.assertEqual("%s/b-a" % self.tmpdir, path)
@staticmethod
def _build_domain(name, action, status):
def _build_domain(name, action, status, id=None):
zid = id or '75ea1626-eea7-46b5-acb7-41e5897c2d40'
values = {
'id': '75ea1626-eea7-46b5-acb7-41e5897c2d40',
'id': zid,
'name': name,
'pool_id': '794ccc2c-d751-44fe-b57f-8894c9f5c842',
'action': action,
@ -112,6 +125,13 @@ class PoolManagerServiceNoopTest(PoolManagerTestCase):
}
return objects.Domain.from_dict(values)
def _build_domains(self, n, action, status):
return [
self._build_domain("zone%02X.example" % cnt, action,
status, id=str(uuid.uuid4()))
for cnt in range(n)
]
@patch.object(mdns_rpcapi.MdnsAPI, 'get_serial_number',
side_effect=messaging.MessagingException)
@patch.object(mdns_rpcapi.MdnsAPI, 'poll_for_serial_number')
@ -434,3 +454,82 @@ class PoolManagerServiceNoopTest(PoolManagerTestCase):
mock_update_status.assert_called_once_with(
self.admin_context, domain.id, 'ERROR', 0)
@patch.object(central_rpcapi.CentralAPI, 'update_status')
def test_update_domain_no_consensus(self, mock_cent_update_status):
zone = self._build_domain('example.org.', 'UPDATE', 'PENDING')
self.service._update_domain_on_target = Mock(return_value=True)
self.service._exceed_or_meet_threshold = Mock(return_value=False)
ret = self.service.update_domain(self.admin_context, zone)
self.assertFalse(ret)
self.assertEqual(2, self.service._update_domain_on_target.call_count)
self.assertEqual(1, mock_cent_update_status.call_count)
@patch.object(mdns_rpcapi.MdnsAPI, 'poll_for_serial_number')
def test_update_domain(self, mock_mdns_poll):
zone = self._build_domain('example.org.', 'UPDATE', 'PENDING')
self.service._update_domain_on_target = Mock(return_value=True)
self.service._update_domain_on_also_notify = Mock()
self.service.pool.also_notifies = ['bogus']
self.service._exceed_or_meet_threshold = Mock(return_value=True)
# cache.retrieve will throw exceptions.PoolManagerStatusNotFound
# mdns_api.poll_for_serial_number will be called twice
ret = self.service.update_domain(self.admin_context, zone)
self.assertTrue(ret)
self.assertEqual(2, self.service._update_domain_on_target.call_count)
self.assertEqual(1, self.service._update_domain_on_also_notify.call_count) # noqa
self.assertEqual(2, mock_mdns_poll.call_count)
@patch.object(mdns_rpcapi.MdnsAPI, 'notify_zone_changed')
@patch.object(central_rpcapi.CentralAPI, 'update_status')
@patch.object(central_rpcapi.CentralAPI, 'find_domains')
def test_periodic_sync(self, mock_find_domains,
mock_cent_update_status, *a):
self.service.update_domain = Mock()
mock_find_domains.return_value = self._build_domains(2, 'UPDATE',
'PENDING')
self.service.periodic_sync()
self.assertEqual(1, mock_find_domains.call_count)
criterion = mock_find_domains.call_args_list[0][0][1]
self.assertEqual('!ERROR', criterion['status'])
self.assertEqual(2, self.service.update_domain.call_count)
self.assertEqual(0, mock_cent_update_status.call_count)
@patch.object(mdns_rpcapi.MdnsAPI, 'notify_zone_changed')
@patch.object(central_rpcapi.CentralAPI, 'update_status')
@patch.object(central_rpcapi.CentralAPI, 'find_domains')
def test_periodic_sync_with_failing_update(self, mock_find_domains,
mock_cent_update_status, *a):
self.service.update_domain = Mock(return_value=False) # fail update
mock_find_domains.return_value = self._build_domains(3, 'UPDATE',
'PENDING')
self.service.periodic_sync()
self.assertEqual(1, mock_find_domains.call_count)
criterion = mock_find_domains.call_args_list[0][0][1]
self.assertEqual('!ERROR', criterion['status'])
# all zones are now in ERROR status
self.assertEqual(3, self.service.update_domain.call_count)
self.assertEqual(3, mock_cent_update_status.call_count)
@patch.object(mdns_rpcapi.MdnsAPI, 'notify_zone_changed')
@patch.object(central_rpcapi.CentralAPI, 'update_status')
@patch.object(central_rpcapi.CentralAPI, 'find_domains')
def test_periodic_sync_with_failing_update_with_exception(
self, mock_find_domains, mock_cent_update_status, *a):
self.service.update_domain = Mock(side_effect=Exception)
mock_find_domains.return_value = self._build_domains(3, 'UPDATE',
'PENDING')
self.service.periodic_sync()
self.assertEqual(1, mock_find_domains.call_count)
criterion = mock_find_domains.call_args_list[0][0][1]
self.assertEqual('!ERROR', criterion['status'])
# the first updated zone is now in ERROR status
self.assertEqual(1, self.service.update_domain.call_count)
self.assertEqual(1, mock_cent_update_status.call_count)

View File

@ -0,0 +1,58 @@
# Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# Author: Federico Ceratto <federico.ceratto@hpe.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from mock import Mock
from mock import MagicMock
from mock import patch
from oslotest import base as test
from designate import exceptions
from designate import objects
from designate.pool_manager.service import Service
class PoolManagerTest(test.BaseTestCase):
def __setUp(self):
super(PoolManagerTest, self).setUp()
def test_init_no_pool_targets(self):
with patch.object(objects.Pool, 'from_config',
return_value=MagicMock()):
self.assertRaises(exceptions.NoPoolTargetsConfigured, Service)
def test_init(self):
with patch.object(objects.Pool, 'from_config',
return_value=Mock()):
Service._setup_target_backends = Mock()
Service()
def test_start(self):
with patch.object(objects.Pool, 'from_config',
return_value=Mock()):
Service._setup_target_backends = Mock()
pm = Service()
pm.pool.targets = ()
pm.tg.add_timer = Mock()
pm._pool_election = Mock()
with patch("designate.service.RPCService.start"):
pm.start()
call1 = pm.tg.add_timer.call_args_list[0][0]
self.assertEqual(120, call1[0])
self.assertEqual(120, call1[-1])
call2 = pm.tg.add_timer.call_args_list[1][0]
self.assertEqual(1800, call2[0])
self.assertEqual(1800, call2[-1])