Don't send heartbeats if Manager reports a problem

This commit adds is_working method to base Manager class that can be
used to indicate that service has some problems and isn't running
correctly. This is used to block refreshing Service heartbeats if
Manager will return False from is_working.

The method is implemented in c-vol Manager to return initialization
state of the underlying volume driver.

Closes-Bug: 1446750
DocImpact

Change-Id: I809b2b777a0f84d12f39f2233920ec961f74f846
This commit is contained in:
Michal Dulko 2015-06-04 17:25:24 +02:00
parent 376f972362
commit 2a84ae0fc0
5 changed files with 61 additions and 0 deletions

View File

@ -112,6 +112,14 @@ class Manager(base.Base, periodic_task.PeriodicTasks):
config[key] = CONF.get(key, None)
return config
def is_working(self):
"""Method indicating if service is working correctly.
This method is supposed to be overriden by subclasses and return if
manager is working correctly.
"""
return True
class SchedulerDependentManager(Manager):
"""Periodically send capability updates to the Scheduler services.

View File

@ -287,6 +287,14 @@ class Service(service.Service):
def report_state(self):
"""Update the state of this service in the datastore."""
if not self.manager.is_working():
# NOTE(dulek): If manager reports a problem we're not sending
# heartbeats - to indicate that service is actually down.
LOG.error(_LE('Manager for service %s is reporting problems, skip '
'sending heartbeat. Service will appear "down".'),
self.binary)
return
ctxt = context.get_admin_context()
zone = CONF.storage_availability_zone
state_catalog = {}

View File

@ -149,6 +149,7 @@ class ServiceTestCase(test.TestCase):
serv.start()
serv.report_state()
self.assertTrue(serv.model_disconnected)
self.assertFalse(mock_db.service_update.called)
def test_report_state_newly_connected(self):
host = 'foo'
@ -176,6 +177,33 @@ class ServiceTestCase(test.TestCase):
serv.report_state()
self.assertFalse(serv.model_disconnected)
self.assertTrue(mock_db.service_update.called)
def test_report_state_manager_not_working(self):
host = 'foo'
binary = 'bar'
topic = 'test'
service_ref = {'host': host,
'binary': binary,
'topic': topic,
'report_count': 0,
'availability_zone': 'nova',
'id': 1}
with mock.patch.object(service, 'db') as mock_db:
mock_db.service_get.return_value = service_ref
serv = service.Service(
host,
binary,
topic,
'cinder.tests.unit.test_service.FakeManager'
)
serv.manager.is_working = mock.Mock(return_value=False)
serv.start()
serv.report_state()
serv.manager.is_working.assert_called_once_with()
self.assertFalse(mock_db.service_update.called)
def test_service_with_long_report_interval(self):
self.override_config('service_down_time', 10)

View File

@ -330,6 +330,14 @@ class VolumeTestCase(BaseVolumeTestCase):
manager.init_host()
self.assertEqual(0, mock_add_p_task.call_count)
def test_is_working(self):
# By default we have driver mocked to be initialized...
self.assertTrue(self.volume.is_working())
# ...lets switch it and check again!
self.volume.driver._initialized = False
self.assertFalse(self.volume.is_working())
def test_create_volume_fails_with_creating_and_downloading_status(self):
"""Test init_host in case of volume.

View File

@ -394,6 +394,15 @@ class VolumeManager(manager.SchedulerDependentManager):
resource={'type': 'driver',
'id': self.driver.__class__.__name__})
def is_working(self):
"""Return if Manager is ready to accept requests.
This is to inform Service class that in case of volume driver
initialization failure the manager is actually down and not ready to
accept any requests.
"""
return self.driver.initialized
def create_volume(self, context, volume_id, request_spec=None,
filter_properties=None, allow_reschedule=True,
snapshot_id=None, image_id=None, source_volid=None,