correctly leave group when process is stopped
ceilometer.notification.NotificationService.stop() use attribute before it is initialized, it will raise exception when start() fails. This patch fixes it by putting initialization in __init__(). Note, also adds coordinator.stop() when service is stopped for polling agents. Change-Id: Ied2f086e1f50950b430095ae7ee89036fd4a89d9 Closes-Bug: #1418793
This commit is contained in:
parent
b7633bdbdd
commit
e5e4ac9997
@ -285,6 +285,11 @@ class AgentManager(os_service.Service):
|
|||||||
self.tg.add_timer(cfg.CONF.coordination.heartbeat,
|
self.tg.add_timer(cfg.CONF.coordination.heartbeat,
|
||||||
self.partition_coordinator.heartbeat)
|
self.partition_coordinator.heartbeat)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
if self.partition_coordinator:
|
||||||
|
self.partition_coordinator.stop()
|
||||||
|
super(AgentManager, self).stop()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def interval_task(task):
|
def interval_task(task):
|
||||||
task.poll_and_publish()
|
task.poll_and_publish()
|
||||||
|
@ -78,6 +78,21 @@ class PartitionCoordinator(object):
|
|||||||
self._started = False
|
self._started = False
|
||||||
LOG.exception(_LE('Error connecting to coordination backend.'))
|
LOG.exception(_LE('Error connecting to coordination backend.'))
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
if not self._coordinator:
|
||||||
|
return
|
||||||
|
|
||||||
|
for group in list(self._groups):
|
||||||
|
self.leave_group(group)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._coordinator.stop()
|
||||||
|
except tooz.coordination.ToozError:
|
||||||
|
LOG.exception(_LE('Error connecting to coordination backend.'))
|
||||||
|
finally:
|
||||||
|
self._coordinator = None
|
||||||
|
self._started = False
|
||||||
|
|
||||||
def is_active(self):
|
def is_active(self):
|
||||||
return self._coordinator is not None
|
return self._coordinator is not None
|
||||||
|
|
||||||
@ -121,8 +136,11 @@ class PartitionCoordinator(object):
|
|||||||
self._groups.add(group_id)
|
self._groups.add(group_id)
|
||||||
|
|
||||||
def leave_group(self, group_id):
|
def leave_group(self, group_id):
|
||||||
|
if group_id not in self._groups:
|
||||||
|
return
|
||||||
if self._coordinator:
|
if self._coordinator:
|
||||||
self._coordinator.leave_group(group_id)
|
self._coordinator.leave_group(group_id)
|
||||||
|
self._groups.remove(group_id)
|
||||||
LOG.info(_LI('Left partitioning group %s'), group_id)
|
LOG.info(_LI('Left partitioning group %s'), group_id)
|
||||||
|
|
||||||
def _get_members(self, group_id):
|
def _get_members(self, group_id):
|
||||||
|
@ -70,6 +70,12 @@ class NotificationService(os_service.Service):
|
|||||||
NOTIFICATION_NAMESPACE = 'ceilometer.notification'
|
NOTIFICATION_NAMESPACE = 'ceilometer.notification'
|
||||||
NOTIFICATION_IPC = 'ceilometer-pipe'
|
NOTIFICATION_IPC = 'ceilometer-pipe'
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(NotificationService, self).__init__(*args, **kwargs)
|
||||||
|
self.partition_coordinator = None
|
||||||
|
self.listeners = self.pipeline_listeners = []
|
||||||
|
self.group_id = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_notifications_manager(cls, transporter):
|
def _get_notifications_manager(cls, transporter):
|
||||||
return extension.ExtensionManager(
|
return extension.ExtensionManager(
|
||||||
@ -201,6 +207,7 @@ class NotificationService(os_service.Service):
|
|||||||
self.pipeline_listeners.append(listener)
|
self.pipeline_listeners.append(listener)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.partition_coordinator.leave_group(self.group_id)
|
if self.partition_coordinator:
|
||||||
|
self.partition_coordinator.stop()
|
||||||
utils.kill_listeners(self.listeners + self.pipeline_listeners)
|
utils.kill_listeners(self.listeners + self.pipeline_listeners)
|
||||||
super(NotificationService, self).stop()
|
super(NotificationService, self).stop()
|
||||||
|
@ -32,6 +32,9 @@ class MockToozCoordinator(object):
|
|||||||
def start(self):
|
def start(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def heartbeat(self):
|
def heartbeat(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -55,6 +58,9 @@ class MockToozCoordinator(object):
|
|||||||
}
|
}
|
||||||
return MockAsyncResult(None)
|
return MockAsyncResult(None)
|
||||||
|
|
||||||
|
def leave_group(self, group_id):
|
||||||
|
return MockAsyncResult(None)
|
||||||
|
|
||||||
def get_members(self, group_id):
|
def get_members(self, group_id):
|
||||||
if group_id not in self._groups:
|
if group_id not in self._groups:
|
||||||
return MockAsyncError(
|
return MockAsyncError(
|
||||||
@ -221,3 +227,23 @@ class TestPartitioning(base.BaseTestCase):
|
|||||||
coord.heartbeat()
|
coord.heartbeat()
|
||||||
for e in expected_errors:
|
for e in expected_errors:
|
||||||
self.assertNotIn(e, self.str_handler.messages['error'])
|
self.assertNotIn(e, self.str_handler.messages['error'])
|
||||||
|
|
||||||
|
def test_group_id_none(self):
|
||||||
|
coord = self._get_new_started_coordinator({}, 'a')
|
||||||
|
self.assertTrue(coord._started)
|
||||||
|
|
||||||
|
with mock.patch.object(coord._coordinator, 'join_group') as mocked:
|
||||||
|
coord.join_group(None)
|
||||||
|
self.assertEqual(0, mocked.call_count)
|
||||||
|
with mock.patch.object(coord._coordinator, 'leave_group') as mocked:
|
||||||
|
coord.leave_group(None)
|
||||||
|
self.assertEqual(0, mocked.call_count)
|
||||||
|
|
||||||
|
def test_stop(self):
|
||||||
|
coord = self._get_new_started_coordinator({}, 'a')
|
||||||
|
self.assertTrue(coord._started)
|
||||||
|
coord.join_group("123")
|
||||||
|
coord.stop()
|
||||||
|
self.assertIsEmpty(coord._groups)
|
||||||
|
self.assertFalse(coord._started)
|
||||||
|
self.assertIsNone(coord._coordinator)
|
||||||
|
@ -28,6 +28,7 @@ from ceilometer.compute.notifications import instance
|
|||||||
from ceilometer import messaging
|
from ceilometer import messaging
|
||||||
from ceilometer import notification
|
from ceilometer import notification
|
||||||
from ceilometer.openstack.common import fileutils
|
from ceilometer.openstack.common import fileutils
|
||||||
|
import ceilometer.openstack.common.service
|
||||||
from ceilometer.publisher import test as test_publisher
|
from ceilometer.publisher import test as test_publisher
|
||||||
from ceilometer.tests import base as tests_base
|
from ceilometer.tests import base as tests_base
|
||||||
|
|
||||||
@ -250,6 +251,14 @@ class TestRealNotification(BaseRealNotification):
|
|||||||
fake_coord.return_value = fake_coord1
|
fake_coord.return_value = fake_coord1
|
||||||
self._check_notification_service()
|
self._check_notification_service()
|
||||||
|
|
||||||
|
@mock.patch.object(ceilometer.openstack.common.service.Service, 'stop')
|
||||||
|
def test_notification_service_start_abnormal(self, mocked):
|
||||||
|
try:
|
||||||
|
self.srv.stop()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
self.assertEqual(1, mocked.call_count)
|
||||||
|
|
||||||
|
|
||||||
class TestRealNotificationHA(BaseRealNotification):
|
class TestRealNotificationHA(BaseRealNotification):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user