aodh/tests/alarm/test_partitioned_alarm_svc.py
Eoghan Glynn ede2329e54 Simple alarm partitioning protocol based on AMQP fanout RPC
All available partitions report their presence periodically.

The priority of each partition in terms of assuming mastership
is determined by earliest start-time (with a UUID-based tiebreaker
in the unlikely event of a time clash).

A single partion assumes mastership at any given time, taking
responsibility for allocating the alarms to be evaluated across
the set of currently available partitions.

When a partition lifecycle event is detected (i.e. a pre-existing
partition fails to report its presence, or a new one is started
up), a complete rebalance of the alarms is initiated.

Individual alarm lifecycle events, on the other hand, do not
require a full re-balance. Instead new alarms are allocated as
they are detected, whereas deleted alarms are initially allowed to
remain within the allocation (as the individual evaluators are tolerant
of assigned alarms not existing, and the deleted alarms should be
randomly distributed over the partions). However once the number of
alarms deleted since the last rebalance reaches a certain limit, a
rebalance will be initiated to maintain equity.

As presence reports are received, each partition keeps track of the
oldest partition it currently knows about, allowing an assumption of
mastership to be aborted if an older partition belatedly reports.

The alarm evaluation service to launch (singleton versus partitioned)
is controlled via a new alarm.evaluation_service config option.

Implements bp alarm-service-partitioner

Change-Id: I3dede464d019a7f776f3d302e2b24cc4a9fc5b66
2013-09-20 17:22:46 +01:00

103 lines
4.0 KiB
Python

# -*- encoding: utf-8 -*-
#
# Copyright © 2013 Red Hat, Inc
#
# Author: Eoghan Glynn <eglynn@redhat.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.
"""Tests for ceilometer.alarm.service.PartitionedAlarmService.
"""
import mock
from contextlib import nested
from stevedore import extension
from stevedore.tests import manager as extension_tests
from oslo.config import cfg
from ceilometer.alarm import service
from ceilometer.tests import base
class TestPartitionedAlarmService(base.TestCase):
def setUp(self):
super(TestPartitionedAlarmService, self).setUp()
self.threshold_eval = mock.Mock()
self.api_client = mock.MagicMock()
cfg.CONF.set_override('host',
'fake_host')
cfg.CONF.set_override('partition_rpc_topic',
'fake_topic',
group='alarm')
self.partitioned = service.PartitionedAlarmService()
self.partitioned.tg = mock.Mock()
self.partitioned.partition_coordinator = mock.Mock()
self.extension_mgr = extension_tests.TestExtensionManager(
[
extension.Extension(
'threshold',
None,
None,
self.threshold_eval, ),
])
self.partitioned.extension_manager = self.extension_mgr
@mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock())
def test_start(self):
test_interval = 120
cfg.CONF.set_override('evaluation_interval',
test_interval,
group='alarm')
get_client = 'ceilometerclient.client.get_client'
create_conn = 'ceilometer.openstack.common.rpc.create_connection'
with nested(mock.patch(get_client, return_value=self.api_client),
mock.patch(create_conn)):
self.partitioned.start()
pc = self.partitioned.partition_coordinator
expected = [
mock.call(test_interval / 4,
pc.report_presence,
0),
mock.call(test_interval / 2,
pc.check_mastership,
test_interval,
test_interval,
self.api_client),
mock.call(test_interval,
self.partitioned._evaluate_assigned_alarms,
test_interval),
mock.call(604800, mock.ANY),
]
actual = self.partitioned.tg.add_timer.call_args_list
self.assertEqual(actual, expected)
def test_presence_reporting(self):
priority = 42
self.partitioned.presence(mock.Mock(),
dict(uuid='uuid', priority=priority))
pc = self.partitioned.partition_coordinator
pc.presence.assert_called_once_with('uuid', priority)
def test_alarm_assignment(self):
alarms = [mock.Mock()]
self.partitioned.assign(mock.Mock(),
dict(uuid='uuid', alarms=alarms))
pc = self.partitioned.partition_coordinator
pc.assign.assert_called_once_with('uuid', alarms)
def test_alarm_allocation(self):
alarms = [mock.Mock()]
self.partitioned.allocate(mock.Mock(),
dict(uuid='uuid', alarms=alarms))
pc = self.partitioned.partition_coordinator
pc.allocate.assert_called_once_with('uuid', alarms)