Set zone in ERROR status on periodic sync fail
Improve testing Change-Id: I1d65ed13fc185858ffe477e4bca6303e01a12c1d Closes-Bug: #1416263
This commit is contained in:
parent
4b240b910d
commit
9d2027f788
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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])
|
Loading…
Reference in New Issue