Use the new alarm format

The change sync the client API with the new alarm type and keep
compatibility with the previous format of the alarm.

It allows to create the new type of alarm: combination alarm.

Implements blueprint alarming-logical-combination

Change-Id: Ie62265023cadc20cf36a0d0b3775f4d984e324cf
This commit is contained in:
Mehdi Abaakouk
2013-09-15 22:26:04 +02:00
committed by Mehdi Abaakouk
parent 052f210f56
commit 3499631b1a
5 changed files with 431 additions and 117 deletions

View File

@@ -140,6 +140,17 @@ def args_array_to_dict(kwargs, key_to_convert):
return kwargs
def key_with_slash_to_nested_dict(kwargs):
nested_kwargs = {}
for k in kwargs.keys():
keys = k.split('/', 1)
if len(keys) == 2:
nested_kwargs.setdefault(keys[0], {})[keys[1]] = kwargs[k]
del kwargs[k]
kwargs.update(nested_kwargs)
return kwargs
def exit(msg=''):
if msg:
print >> sys.stderr, msg

View File

@@ -57,3 +57,20 @@ class UtilsTest(test_utils.BaseTestCase):
'matching_metadata': {'metadata.key': 'metadata_value'},
'other': 'value'
})
def test_key_with_slash_to_nested_dict(self):
my_args = {
'combination_rule/alarm_ids': ['id1', 'id2'],
'combination_rule/operator': 'and',
'threshold_rule/threshold': 400,
'threshold_rule/statictic': 'avg',
'threshold_rule/comparison_operator': 'or',
}
nested_dict = utils.key_with_slash_to_nested_dict(my_args)
self.assertEqual(nested_dict, {
'combination_rule': {'alarm_ids': ['id1', 'id2'],
'operator': 'and'},
'threshold_rule': {'threshold': 400,
'statictic': 'avg',
'comparison_operator': 'or'},
})

View File

@@ -22,37 +22,91 @@ import testtools
from ceilometerclient.tests import utils
import ceilometerclient.v2.alarms
AN_ALARM = {u'alarm_actions': [u'http://site:8000/alarm'],
u'ok_actions': [u'http://site:8000/ok'],
u'description': u'An alarm',
u'matching_metadata': {u'key_name': u'key_value'},
u'evaluation_periods': 2,
u'type': u'threshold',
u'threshold_rule': {
u'meter_name': u'storage.objects',
u'query': [{u'field': u'key_name',
u'op': u'eq',
u'value': u'key_value'}],
u'evaluation_periods': 2,
u'period': 240.0,
u'statistic': u'avg',
u'threshold': 200.0,
u'comparison_operator': 'gt',
},
u'timestamp': u'2013-05-09T13:41:23.085000',
u'enabled': True,
u'meter_name': u'storage.objects',
u'period': 240.0,
u'alarm_id': u'alarm-id',
u'state': u'ok',
u'insufficient_data_actions': [u'http://site:8000/nodata'],
u'statistic': u'avg',
u'threshold': 200.0,
u'user_id': u'user-id',
u'project_id': u'project-id',
u'state_timestamp': u'2013-05-09T13:41:23.085000',
u'comparison_operator': 'gt',
u'repeat_actions': False,
u'name': 'SwiftObjectAlarm'}
CREATE_ALARM = copy.deepcopy(AN_ALARM)
del CREATE_ALARM['timestamp']
del CREATE_ALARM['state_timestamp']
del CREATE_ALARM['alarm_id']
DELTA_ALARM = {u'alarm_actions': ['url1', 'url2'],
u'comparison_operator': u'lt',
u'meter_name': u'foobar',
u'threshold': 42.1}
DELTA_ALARM = {u'alarm_actions': ['url1', 'url2']}
DELTA_ALARM_RULE = {u'comparison_operator': u'lt',
u'threshold': 42.1,
u'meter_name': u'foobar',
u'query': [{u'field': u'key_name',
u'op': u'eq',
u'value': u'key_value'}]}
UPDATED_ALARM = copy.deepcopy(AN_ALARM)
UPDATED_ALARM.update(DELTA_ALARM)
UPDATED_ALARM['threshold_rule'].update(DELTA_ALARM_RULE)
UPDATE_ALARM = copy.deepcopy(UPDATED_ALARM)
del UPDATE_ALARM['user_id']
del UPDATE_ALARM['project_id']
del UPDATE_ALARM['name']
del UPDATE_ALARM['alarm_id']
del UPDATE_ALARM['timestamp']
del UPDATE_ALARM['state_timestamp']
AN_LEGACY_ALARM = {u'alarm_actions': [u'http://site:8000/alarm'],
u'ok_actions': [u'http://site:8000/ok'],
u'description': u'An alarm',
u'matching_metadata': {u'key_name': u'key_value'},
u'evaluation_periods': 2,
u'timestamp': u'2013-05-09T13:41:23.085000',
u'enabled': True,
u'meter_name': u'storage.objects',
u'period': 240.0,
u'alarm_id': u'alarm-id',
u'state': u'ok',
u'insufficient_data_actions': [u'http://site:8000/nodata'],
u'statistic': u'avg',
u'threshold': 200.0,
u'user_id': u'user-id',
u'project_id': u'project-id',
u'state_timestamp': u'2013-05-09T13:41:23.085000',
u'comparison_operator': 'gt',
u'repeat_actions': False,
u'name': 'SwiftObjectAlarm'}
CREATE_LEGACY_ALARM = copy.deepcopy(AN_LEGACY_ALARM)
del CREATE_LEGACY_ALARM['timestamp']
del CREATE_LEGACY_ALARM['state_timestamp']
del CREATE_LEGACY_ALARM['alarm_id']
DELTA_LEGACY_ALARM = {u'alarm_actions': ['url1', 'url2'],
u'comparison_operator': u'lt',
u'meter_name': u'foobar',
u'threshold': 42.1}
UPDATED_LEGACY_ALARM = copy.deepcopy(AN_LEGACY_ALARM)
UPDATED_LEGACY_ALARM.update(DELTA_LEGACY_ALARM)
UPDATE_LEGACY_ALARM = copy.deepcopy(UPDATED_LEGACY_ALARM)
del UPDATE_LEGACY_ALARM['user_id']
del UPDATE_LEGACY_ALARM['project_id']
del UPDATE_LEGACY_ALARM['name']
del UPDATE_LEGACY_ALARM['alarm_id']
del UPDATE_LEGACY_ALARM['timestamp']
del UPDATE_LEGACY_ALARM['state_timestamp']
fixtures = {
'/v2/alarms':
@@ -77,6 +131,18 @@ fixtures = {
UPDATED_ALARM,
),
},
'/v2/alarms/alarm-id/state':
{
'PUT': (
{},
'alarm'
),
'GET': (
{},
'alarm'
),
},
'/v2/alarms?q.op=&q.op=&q.value=project-id&q.value=SwiftObjectAlarm'
'&q.field=project_id&q.field=name':
{
@@ -136,6 +202,7 @@ class AlarmManagerTest(testtools.TestCase):
self.assertEqual(self.api.calls, expect)
self.assertTrue(alarm)
self.assertEqual(alarm.alarm_id, 'alarm-id')
self.assertEqual(alarm.rule, alarm.threshold_rule)
def test_create(self):
alarm = self.mgr.create(**CREATE_ALARM)
@@ -145,10 +212,61 @@ class AlarmManagerTest(testtools.TestCase):
self.assertEqual(self.api.calls, expect)
self.assertTrue(alarm)
def test_create_legacy(self):
def test_update(self):
alarm = self.mgr.update(alarm_id='alarm-id', **UPDATE_ALARM)
expect = [
('PUT', '/v2/alarms/alarm-id', {}, UPDATE_ALARM),
]
self.assertEqual(self.api.calls, expect)
self.assertTrue(alarm)
self.assertEqual(alarm.alarm_id, 'alarm-id')
for (key, value) in UPDATED_ALARM.iteritems():
self.assertEqual(getattr(alarm, key), value)
def test_set_state(self):
state = self.mgr.set_state(alarm_id='alarm-id', state='alarm')
expect = [
('PUT', '/v2/alarms/alarm-id/state', {}, 'alarm'),
]
self.assertEqual(self.api.calls, expect)
self.assertEqual(state, 'alarm')
def test_get_state(self):
state = self.mgr.get_state(alarm_id='alarm-id')
expect = [
('GET', '/v2/alarms/alarm-id/state', {}, None),
]
self.assertEqual(self.api.calls, expect)
self.assertEqual(state, 'alarm')
def test_delete(self):
deleted = self.mgr.delete(alarm_id='victim-id')
expect = [
('DELETE', '/v2/alarms/victim-id', {}, None),
]
self.assertEqual(self.api.calls, expect)
self.assertTrue(deleted is None)
class AlarmLegacyManagerTest(testtools.TestCase):
def setUp(self):
super(AlarmLegacyManagerTest, self).setUp()
self.api = utils.FakeAPI(fixtures)
self.mgr = ceilometerclient.v2.alarms.AlarmManager(self.api)
def test_create(self):
alarm = self.mgr.create(**CREATE_LEGACY_ALARM)
expect = [
('POST', '/v2/alarms', {}, CREATE_ALARM),
]
self.assertEqual(self.api.calls, expect)
self.assertTrue(alarm)
def test_create_counter_name(self):
create = {}
create.update(CREATE_ALARM)
create['counter_name'] = CREATE_ALARM['meter_name']
create.update(CREATE_LEGACY_ALARM)
create['counter_name'] = CREATE_LEGACY_ALARM['meter_name']
del create['meter_name']
alarm = self.mgr.create(**create)
expect = [
@@ -158,35 +276,27 @@ class AlarmManagerTest(testtools.TestCase):
self.assertTrue(alarm)
def test_update(self):
alarm = self.mgr.update(alarm_id='alarm-id', **DELTA_ALARM)
alarm = self.mgr.update(alarm_id='alarm-id', **UPDATE_LEGACY_ALARM)
expect = [
('PUT', '/v2/alarms/alarm-id', {}, DELTA_ALARM),
('PUT', '/v2/alarms/alarm-id', {}, UPDATE_ALARM),
]
self.assertEqual(self.api.calls, expect)
self.assertTrue(alarm)
self.assertEqual(alarm.alarm_id, 'alarm-id')
for (key, value) in DELTA_ALARM.iteritems():
for (key, value) in UPDATED_ALARM.iteritems():
self.assertEqual(getattr(alarm, key), value)
def test_update_legacy(self):
delta = {}
delta.update(DELTA_ALARM)
delta['counter_name'] = DELTA_ALARM['meter_name']
del delta['meter_name']
alarm = self.mgr.update(alarm_id='alarm-id', **delta)
def test_update_counter_name(self):
updated = {}
updated.update(UPDATE_LEGACY_ALARM)
updated['counter_name'] = UPDATED_LEGACY_ALARM['meter_name']
del updated['meter_name']
alarm = self.mgr.update(alarm_id='alarm-id', **updated)
expect = [
('PUT', '/v2/alarms/alarm-id', {}, DELTA_ALARM),
('PUT', '/v2/alarms/alarm-id', {}, UPDATE_ALARM),
]
self.assertEqual(self.api.calls, expect)
self.assertTrue(alarm)
self.assertEqual(alarm.alarm_id, 'alarm-id')
for (key, value) in DELTA_ALARM.iteritems():
for (key, value) in UPDATED_ALARM.iteritems():
self.assertEqual(getattr(alarm, key), value)
def test_delete(self):
deleted = self.mgr.delete(alarm_id='victim-id')
expect = [
('DELETE', '/v2/alarms/victim-id', {}, None),
]
self.assertEqual(self.api.calls, expect)
self.assertTrue(deleted is None)

View File

@@ -23,28 +23,32 @@ from ceilometerclient.v2 import options
UPDATABLE_ATTRIBUTES = [
'name',
'description',
'period',
'evaluation_periods',
'type',
'state',
'enabled',
'meter_name',
'statistic',
'comparison_operator',
'threshold',
'alarm_actions',
'ok_actions',
'insufficient_data_actions',
'repeat_actions',
'matching_metadata',
'threshold_rule',
'combination_rule',
]
CREATION_ATTRIBUTES = UPDATABLE_ATTRIBUTES + ['name', 'project_id', 'user_id']
CREATION_ATTRIBUTES = UPDATABLE_ATTRIBUTES + ['project_id', 'user_id']
class Alarm(base.Resource):
def __repr__(self):
return "<Alarm %s>" % self._info
def __getattr__(self, k):
# Alias to have the Alarm client object
# that look like the Alarm storage object
if k == 'rule':
k = '%s_rule' % self.type
return super(Alarm, self).__getattr__(k)
class AlarmManager(base.Manager):
resource_class = Alarm
@@ -62,6 +66,11 @@ class AlarmManager(base.Manager):
except IndexError:
return None
@classmethod
def _compat_legacy_alarm_kwargs(cls, kwargs):
cls._compat_counter_rename_kwargs(kwargs)
cls._compat_alarm_before_rule_type_kwargs(kwargs)
@staticmethod
def _compat_counter_rename_kwargs(kwargs):
# NOTE(jd) Compatibility with Havana-2 API
@@ -70,17 +79,53 @@ class AlarmManager(base.Manager):
DeprecationWarning)
kwargs['meter_name'] = kwargs['counter_name']
@staticmethod
def _compat_alarm_before_rule_type_kwargs(kwargs):
# NOTE(sileht) Compatibility with Havana-3 API
if kwargs.get('type'):
return
warnings.warn("alarm without type set is deprecated",
DeprecationWarning)
kwargs['type'] = 'threshold'
kwargs['threshold_rule'] = {}
for field in ['period', 'evaluation_periods', 'threshold',
'statistic', 'comparison_operator', 'meter_name']:
if field in kwargs:
kwargs['threshold_rule'][field] = kwargs[field]
del kwargs[field]
query = []
if 'matching_metadata' in kwargs:
for key in kwargs['matching_metadata']:
query.append({'field': key,
'op': 'eq',
'value': kwargs['matching_metadata'][key]})
del kwargs['matching_metadata']
kwargs['threshold_rule']['query'] = query
def create(self, **kwargs):
self._compat_counter_rename_kwargs(kwargs)
self._compat_legacy_alarm_kwargs(kwargs)
new = dict((key, value) for (key, value) in kwargs.items()
if key in CREATION_ATTRIBUTES)
return self._create(self._path(), new)
def update(self, alarm_id, **kwargs):
self._compat_counter_rename_kwargs(kwargs)
self._compat_legacy_alarm_kwargs(kwargs)
updated = dict((key, value) for (key, value) in kwargs.items()
if key in UPDATABLE_ATTRIBUTES)
return self._update(self._path(alarm_id), updated)
def delete(self, alarm_id):
return self._delete(self._path(alarm_id))
def set_state(self, alarm_id, state):
resp, body = self.api.json_request('PUT',
"%s/state" % self._path(alarm_id),
body=state)
return body
def get_state(self, alarm_id):
resp, body = self.api.json_request('GET',
"%s/state" % self._path(alarm_id))
return body

View File

@@ -16,6 +16,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import functools
import json
from ceilometerclient.common import utils
@@ -25,6 +26,7 @@ from ceilometerclient.v2 import options
ALARM_STATES = ['ok', 'alarm', 'insufficient_data']
ALARM_OPERATORS = ['lt', 'le', 'eq', 'ne', 'ge', 'gt']
ALARM_COMBINATION_OPERATORS = ['and', 'or']
STATISTICS = ['max', 'min', 'avg', 'sum', 'count']
@@ -133,23 +135,19 @@ def do_alarm_list(cc, args={}):
alarms = cc.alarms.list(q=options.cli_to_array(args.query))
# omit action initially to keep output width sane
# (can switch over to vertical formatting when available from CLIFF)
field_labels = ['Name', 'Description', 'Metric', 'Period', 'Count',
'Threshold', 'Comparison', 'State', 'Enabled', 'Alarm ID',
'User ID', 'Project ID']
fields = ['name', 'description', 'meter_name', 'period',
'evaluation_periods', 'threshold', 'comparison_operator',
'state', 'enabled', 'alarm_id', 'user_id', 'project_id']
field_labels = ['Name', 'Description', 'State', 'Enabled', 'Continuous',
'Alarm ID', 'User ID', 'Project ID']
fields = ['name', 'description', 'state', 'enabled', 'repeat_actions',
'alarm_id', 'user_id', 'project_id']
utils.print_list(alarms, fields, field_labels,
sortby=0)
def _display_alarm(alarm):
fields = ['name', 'description', 'meter_name', 'period',
'evaluation_periods', 'statistic', 'threshold',
'comparison_operator', 'state', 'enabled', 'alarm_id', 'user_id',
'project_id', 'alarm_actions', 'ok_actions',
'insufficient_data_actions', 'repeat_actions',
'matching_metadata']
fields = ['name', 'description', 'type', 'rule',
'state', 'enabled', 'alarm_id', 'user_id', 'project_id',
'alarm_actions', 'ok_actions', 'insufficient_data_actions',
'repeat_actions']
data = dict([(f, getattr(alarm, f, '')) for f in fields])
utils.print_dict(data, wrap=72)
@@ -168,96 +166,136 @@ def do_alarm_show(cc, args={}):
_display_alarm(alarm)
@utils.arg('--name', metavar='<NAME>',
help='Name of the alarm (must be unique per tenant)')
@utils.arg('--project-id', metavar='<PROJECT_ID>',
help='Tenant to associate with alarm '
'(only settable by admin users)')
@utils.arg('--user-id', metavar='<USER_ID>',
help='User to associate with alarm '
'(only settable by admin users)')
@utils.arg('--description', metavar='<DESCRIPTION>',
help='Free text description of the alarm')
def common_alarm_arguments(func):
@utils.arg('--name', metavar='<NAME>', required=True,
help='Name of the alarm (must be unique per tenant)')
@utils.arg('--project-id', metavar='<PROJECT_ID>',
help='Tenant to associate with alarm '
'(only settable by admin users)')
@utils.arg('--user-id', metavar='<USER_ID>',
help='User to associate with alarm '
'(only settable by admin users)')
@utils.arg('--description', metavar='<DESCRIPTION>',
help='Free text description of the alarm')
@utils.arg('--state', metavar='<STATE>',
help='State of the alarm, one of: ' + str(ALARM_STATES))
@utils.arg('--enabled', type=utils.string_to_bool, metavar='{True|False}',
help='True if alarm evaluation/actioning is enabled')
@utils.arg('--alarm-action', dest='alarm_actions',
metavar='<Webhook URL>', action='append', default=None,
help=('URL to invoke when state transitions to alarm. '
'May be used multiple times.'))
@utils.arg('--ok-action', dest='ok_actions',
metavar='<Webhook URL>', action='append', default=None,
help=('URL to invoke when state transitions to OK. '
'May be used multiple times.'))
@utils.arg('--insufficient-data-action', dest='insufficient_data_actions',
metavar='<Webhook URL>', action='append', default=None,
help=('URL to invoke when state transitions to unkown. '
'May be used multiple times.'))
@utils.arg('--repeat-actions', dest='repeat_actions',
metavar='{True|False}', type=utils.string_to_bool,
default=False,
help=('True if actions should be repeatedly notified '
'while alarm remains in target state'))
@functools.wraps(func)
def _wrapper(*args, **kwargs):
return func(*args, **kwargs)
return _wrapper
@common_alarm_arguments
@utils.arg('--period', type=int, metavar='<PERIOD>',
help='Length of each period (seconds) to evaluate over')
@utils.arg('--evaluation-periods', type=int, metavar='<COUNT>',
help='Number of periods to evaluate over')
@utils.arg('--state', metavar='<STATE>',
help='State of the alarm, one of: ' + str(ALARM_STATES))
@utils.arg('--enabled', type=utils.string_to_bool, metavar='{True|False}',
help='True if alarm evaluation/actioning is enabled')
@utils.arg('--meter-name', metavar='<METRIC>',
@utils.arg('--meter-name', metavar='<METRIC>', required=True,
help='Metric to evaluate against')
@utils.arg('--statistic', metavar='<STATISTIC>',
help='Statistic to evaluate, one of: ' + str(STATISTICS))
@utils.arg('--comparison-operator', metavar='<OPERATOR>',
help='Operator to compare with, one of: ' + str(ALARM_OPERATORS))
@utils.arg('--threshold', type=float, metavar='<THRESHOLD>',
@utils.arg('--threshold', type=float, metavar='<THRESHOLD>', required=True,
help='Threshold to evaluate against')
@utils.arg('--alarm-action', dest='alarm_actions',
metavar='<Webhook URL>', action='append', default=None,
help=('URL to invoke when state transitions to alarm. '
'May be used multiple times.'))
@utils.arg('--ok-action', dest='ok_actions',
metavar='<Webhook URL>', action='append', default=None,
help=('URL to invoke when state transitions to OK. '
'May be used multiple times.'))
@utils.arg('--insufficient-data-action', dest='insufficient_data_actions',
metavar='<Webhook URL>', action='append', default=None,
help=('URL to invoke when state transitions to unkown. '
'May be used multiple times.'))
@utils.arg('--repeat-actions', dest='repeat_actions', metavar='{True|False}',
type=utils.string_to_bool, default=False,
help=('True if actions should be repeatedly notified '
'while alarm remains in target state'))
@utils.arg('--matching-metadata', dest='matching_metadata',
metavar='<Matching Metadata>', action='append', default=None,
help=('A meter should match this resource metadata (key=value) '
'additionally to the meter_name'))
def do_alarm_create(cc, args={}):
'''Create a new alarm.'''
'''Create a new alarm (Deprecated).'''
fields = dict(filter(lambda x: not (x[1] is None), vars(args).items()))
fields = utils.args_array_to_dict(fields, "matching_metadata")
alarm = cc.alarms.create(**fields)
_display_alarm(alarm)
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>',
@common_alarm_arguments
@utils.arg('--meter-name', metavar='<METRIC>', required=True,
dest='threshold_rule/meter_name',
help='Metric to evaluate against')
@utils.arg('--period', type=int, metavar='<PERIOD>',
dest='threshold_rule/period',
help='Length of each period (seconds) to evaluate over')
@utils.arg('--evaluation-periods', type=int, metavar='<COUNT>',
dest='threshold_rule/evaluation_periods',
help='Number of periods to evaluate over')
@utils.arg('--statistic', metavar='<STATISTIC>',
dest='threshold_rule/statistic',
help='Statistic to evaluate, one of: ' + str(STATISTICS))
@utils.arg('--comparison-operator', metavar='<OPERATOR>',
dest='threshold_rule/comparison_operator',
help='Operator to compare with, one of: ' + str(ALARM_OPERATORS))
@utils.arg('--threshold', type=float, metavar='<THRESHOLD>', required=True,
dest='threshold_rule/threshold',
help='Threshold to evaluate against')
@utils.arg('-q', '--query', metavar='<QUERY>',
dest='threshold_rule/query',
help='The query to find the data for computing statistics '
'(key[op]value; list.)')
def do_alarm_threshold_create(cc, args={}):
'''Create a new alarm based on computed statistics.'''
fields = dict(filter(lambda x: not (x[1] is None), vars(args).items()))
fields = utils.key_with_slash_to_nested_dict(fields)
fields['type'] = 'threshold'
if 'query' in fields['threshold_rule']:
fields['threshold_rule']['query'] = options.cli_to_array(
fields['threshold_rule']['query'])
alarm = cc.alarms.create(**fields)
_display_alarm(alarm)
@common_alarm_arguments
@utils.arg('--alarm_ids', action='append', metavar='<ALARM IDS>',
required=True, dest='combination_rule/alarm_ids',
help='List of alarm id')
@utils.arg('--operator', metavar='<OPERATOR>',
dest='combination_rule/operator',
help='Operator to compare with, one of: ' + str(
ALARM_COMBINATION_OPERATORS))
def do_alarm_combination_create(cc, args={}):
'''Create a new alarm based on state of other alarms.'''
fields = dict(filter(lambda x: not (x[1] is None), vars(args).items()))
fields = utils.key_with_slash_to_nested_dict(fields)
fields['type'] = 'combination'
alarm = cc.alarms.create(**fields)
_display_alarm(alarm)
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True,
help='ID of the alarm to update.')
@utils.arg('--description', metavar='<DESCRIPTION>',
help='Free text description of the alarm')
@common_alarm_arguments
@utils.arg('--period', type=int, metavar='<PERIOD>',
help='Length of each period (seconds) to evaluate over')
@utils.arg('--evaluation-periods', type=int, metavar='<COUNT>',
help='Number of periods to evaluate over')
@utils.arg('--state', metavar='<STATE>',
help='State of the alarm, one of: ' + str(ALARM_STATES))
@utils.arg('--enabled', type=utils.string_to_bool, metavar='{True|False}',
help='True if alarm evaluation/actioning is enabled')
@utils.arg('--meter-name', metavar='<METRIC>',
@utils.arg('--meter-name', metavar='<METRIC>', required=True,
help='Metric to evaluate against')
@utils.arg('--statistic', metavar='<STATISTIC>',
help='Statistic to evaluate, one of: ' + str(STATISTICS))
@utils.arg('--comparison-operator', metavar='<OPERATOR>',
help='Operator to compare with, one of: ' + str(ALARM_OPERATORS))
@utils.arg('--threshold', type=float, metavar='<THRESHOLD>',
@utils.arg('--threshold', type=float, metavar='<THRESHOLD>', required=True,
help='Threshold to evaluate against')
@utils.arg('--alarm-action', dest='alarm_actions',
metavar='<Webhook URL>', action='append', default=None,
help=('URL to invoke when state transitions to alarm. '
'May be used multiple times.'))
@utils.arg('--ok-action', dest='ok_actions',
metavar='<Webhook URL>', action='append', default=None,
help=('URL to invoke when state transitions to OK. '
'May be used multiple times.'))
@utils.arg('--insufficient-data-action', dest='insufficient_data_actions',
metavar='<Webhook URL>', action='append', default=None,
help=('URL to invoke when state transitions to unkown. '
'May be used multiple times.'))
@utils.arg('--repeat-actions', dest='repeat_actions',
metavar='{True|False}', type=utils.string_to_bool,
help=('True if actions should be repeatedly notified '
'while alarm remains in target state'))
@utils.arg('--matching-metadata', dest='matching_metadata',
metavar='<Matching Metadata>', action='append', default=None,
help=('A meter should match this resource metadata (key=value) '
@@ -267,12 +305,79 @@ def do_alarm_update(cc, args={}):
fields = dict(filter(lambda x: not (x[1] is None), vars(args).items()))
fields = utils.args_array_to_dict(fields, "matching_metadata")
fields.pop('alarm_id')
alarm = cc.alarms.update(args.alarm_id, **fields)
try:
alarm = cc.alarms.update(args.alarm_id, **fields)
except exc.HTTPNotFound:
raise exc.CommandError('Alarm not found: %s' % args.alarm_id)
_display_alarm(alarm)
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>',
help='ID of the alarm to show.')
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True,
help='ID of the alarm to update.')
@common_alarm_arguments
@utils.arg('--meter-name', metavar='<METRIC>',
dest='threshold_rule/meter_name', required=True,
help='Metric to evaluate against')
@utils.arg('--period', type=int, metavar='<PERIOD>',
dest='threshold_rule/period',
help='Length of each period (seconds) to evaluate over')
@utils.arg('--evaluation-periods', type=int, metavar='<COUNT>',
dest='threshold_rule/evaluation_periods',
help='Number of periods to evaluate over')
@utils.arg('--statistic', metavar='<STATISTIC>',
dest='threshold_rule/statistic',
help='Statistic to evaluate, one of: ' + str(STATISTICS))
@utils.arg('--comparison-operator', metavar='<OPERATOR>',
dest='threshold_rule/comparison_operator',
help='Operator to compare with, one of: ' + str(ALARM_OPERATORS))
@utils.arg('--threshold', type=float, metavar='<THRESHOLD>', required=True,
dest='threshold_rule/threshold',
help='Threshold to evaluate against')
@utils.arg('-q', '--query', metavar='<QUERY>',
dest='threshold_rule/query',
help='The query to find the data for computing statistics '
'(key[op]value; list.)')
def do_alarm_threshold_update(cc, args={}):
'''Update an existing alarm based on computed statistics.'''
fields = dict(filter(lambda x: not (x[1] is None), vars(args).items()))
fields = utils.key_with_slash_to_nested_dict(fields)
fields.pop('alarm_id')
fields['type'] = 'threshold'
if 'query' in fields['threshold_rule']:
fields['threshold_rule']['query'] = options.cli_to_array(
fields['threshold_rule']['query'])
try:
alarm = cc.alarms.update(args.alarm_id, **fields)
except exc.HTTPNotFound:
raise exc.CommandError('Alarm not found: %s' % args.alarm_id)
_display_alarm(alarm)
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True,
help='ID of the alarm to update.')
@common_alarm_arguments
@utils.arg('--alarm_ids', action='append', metavar='<ALARM IDS>',
dest='combination_rule/alarm_ids', required=True,
help='List of alarm id')
@utils.arg('---operator', metavar='<OPERATOR>',
dest='combination_rule/operator',
help='Operator to compare with, one of: ' + str(
ALARM_COMBINATION_OPERATORS))
def do_alarm_combination_update(cc, args={}):
'''Update an existing alarm based on state of other alarms.'''
fields = dict(filter(lambda x: not (x[1] is None), vars(args).items()))
fields = utils.key_with_slash_to_nested_dict(fields)
fields.pop('alarm_id')
fields['type'] = 'combination'
try:
alarm = cc.alarms.update(args.alarm_id, **fields)
except exc.HTTPNotFound:
raise exc.CommandError('Alarm not found: %s' % args.alarm_id)
_display_alarm(alarm)
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True,
help='ID of the alarm to delete.')
def do_alarm_delete(cc, args={}):
'''Delete an alarm.'''
if args.alarm_id is None:
@@ -283,6 +388,32 @@ def do_alarm_delete(cc, args={}):
raise exc.CommandError('Alarm not found: %s' % args.alarm_id)
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True,
help='ID of the alarm state to set.')
@utils.arg('--state', metavar='<STATE>', required=True,
help='State of the alarm, one of: ' + str(ALARM_STATES))
def do_alarm_set_state(cc, args={}):
'''Set the state of an alarm.'''
try:
cc.alarms.set_state(args.alarm_id, args.state)
except exc.HTTPNotFound:
raise exc.CommandError('Alarm not found: %s' % args.alarm_id)
state = cc.alarms.get_state(args.alarm_id)
utils.print_dict({'state': state}, wrap=72)
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True,
help='ID of the alarm state to show.')
def do_alarm_get_state(cc, args={}):
'''Get the state of an alarm.'''
try:
cc.alarms.set_state(args.alarm_id, args.state)
except exc.HTTPNotFound:
raise exc.CommandError('Alarm not found: %s' % args.alarm_id)
state = cc.alarms.get_state(args.alarm_id)
utils.print_dict({'state': state}, wrap=72)
@utils.arg('-q', '--query', metavar='<QUERY>',
help='key[op]value; list.')
def do_resource_list(cc, args={}):