summaryrefslogtreecommitdiff
path: root/ceilometerclient
diff options
context:
space:
mode:
authorUros Jovanovic <uros.jovanovic@gmail.com>2014-03-05 18:49:14 +0000
committerUros Jovanovic <uros.jovanovic@gmail.com>2014-03-26 08:24:56 +0000
commit7146571b702579fa1367598ab278ca2f8ec1e156 (patch)
tree051046a88aca58b13ef56a7747a2d4e689e369e9 /ceilometerclient
parent3e8045c9caad407b38758da7f611982809e3a0f5 (diff)
downloadpython-ceilometerclient-7146571b702579fa1367598ab278ca2f8ec1e156.tar.gz
Adds alarm time constraint support to ceilometer CLI
Time constraints can be specified for create and update families of commands with the following format: --time-constraint name=constraint1;start='0 11 * * *';duration=300 This switch can be specified multiple times in the case of multiple time constraints. With update commands, time constraints are updated by name, e.g. --time-constraint name=constraint1;duration=500 updates the constraint 'constraint1' with a new duration 500. Time constraints can be removed with update commands using the switch --remove-time-constraint=constraint1,constraint2 . Example of display outputs: > ceilometer alarm-list +--------------------------------------+-------+-------------------+---------+------------+----------------------------+--------------------------------------------------------------+ | Alarm ID | Name | State | Enabled | Continuous | Alarm condition | Time constraints | +--------------------------------------+-------+-------------------+---------+------------+----------------------------+--------------------------------------------------------------+ | 2ead776d-2fc7-47a2-b0bb-0f88dcefa457 | test2 | insufficient data | True | False | cpu == 50.0 during 1 x 60s | cons1 at 0 11 * * * for 300s, cons2 at 0 23 * * * for 600s | +--------------------------------------+-------+-------------------+---------+------------+----------------------------+--------------------------------------------------------------+ > ceilometer alarm-show -a +---------------------------+-----------------------------------------------------------------------+ | Property | Value | +---------------------------+-----------------------------------------------------------------------+ | alarm_actions | [] | | alarm_id | 2ead776d-2fc7-47a2-b0bb-0f88dcefa457 | | comparison_operator | eq | | description | Alarm when cpu is eq a avg of 50.0 over 60 seconds | | enabled | True | | evaluation_periods | 1 | | exclude_outliers | False | | insufficient_data_actions | [] | | meter_name | cpu | | name | test2 | | ok_actions | [] | | period | 60 | | project_id | 962f75ad22c24cbf99d40d7b82718505 | | query | | | repeat_actions | False | | state | insufficient data | | statistic | avg | | threshold | 50.0 | | time_constraints | [{name: cons1, | | | description: Time constraint at 0 11 * * * lasting for 300 seconds, | | | start: 0 11 * * *, | | | duration: 300}, | | | {name: cons2, | | | description: Time constraint at 0 23 * * * lasting for 600 seconds, | | | start: 0 23 * * *, | | | duration: 600}] | | type | threshold | | user_id | 76f335df8e2f4c7e9e8185e26ea85759 | +---------------------------+-----------------------------------------------------------------------+ > ceilometer alarm-history -a 2ead776d-2fc7-47a2-b0bb-0f88dcefa457 +----------+----------------------------+--------------------------------------------------------------------------------+ | Type | Timestamp | Detail | +----------+----------------------------+--------------------------------------------------------------------------------+ | creation | 2014-03-06T07:41:35.362050 | name: test2 | | | | description: Alarm when cpu is eq a avg of 50.0 over 60 seconds | | | | type: threshold | | | | rule: cpu == 50.0 during 1 x 60s | | | | time_constraints: cons1 at 0 11 * * * for 300s, cons2 at 0 23 * * * for 600s | +----------+----------------------------+--------------------------------------------------------------------------------+ Change-Id: I3953276537b4526e46e5e6d229d6fa154f8ab0fc Closes-Bug: #1288246
Diffstat (limited to 'ceilometerclient')
-rw-r--r--ceilometerclient/common/utils.py21
-rw-r--r--ceilometerclient/tests/test_utils.py28
-rw-r--r--ceilometerclient/tests/v2/test_alarms.py67
-rw-r--r--ceilometerclient/tests/v2/test_shell.py40
-rw-r--r--ceilometerclient/v2/alarms.py25
-rw-r--r--ceilometerclient/v2/shell.py75
6 files changed, 249 insertions, 7 deletions
diff --git a/ceilometerclient/common/utils.py b/ceilometerclient/common/utils.py
index 2628ca0..db7b042 100644
--- a/ceilometerclient/common/utils.py
+++ b/ceilometerclient/common/utils.py
@@ -154,6 +154,27 @@ def args_array_to_dict(kwargs, key_to_convert):
return kwargs
+def args_array_to_list_of_dicts(kwargs, key_to_convert):
+ """Converts ['a=1;b=2','c=3;d=4'] to [{a:1,b:2},{c:3,d:4}]
+ """
+ values_to_convert = kwargs.get(key_to_convert)
+ if values_to_convert:
+ try:
+ kwargs[key_to_convert] = []
+ for lst in values_to_convert:
+ pairs = lst.split(";")
+ dct = dict()
+ for pair in pairs:
+ kv = pair.split("=", 1)
+ dct[kv[0]] = kv[1].strip(" \"'") # strip spaces and quotes
+ kwargs[key_to_convert].append(dct)
+ except Exception:
+ raise exc.CommandError(
+ '%s must be a list of key1=value1;key2=value2;... not "%s"' % (
+ key_to_convert, values_to_convert))
+ return kwargs
+
+
def key_with_slash_to_nested_dict(kwargs):
nested_kwargs = {}
for k in list(kwargs):
diff --git a/ceilometerclient/tests/test_utils.py b/ceilometerclient/tests/test_utils.py
index 1f3074b..9082328 100644
--- a/ceilometerclient/tests/test_utils.py
+++ b/ceilometerclient/tests/test_utils.py
@@ -14,6 +14,7 @@
# under the License.
+import itertools
import mock
import six
import sys
@@ -117,6 +118,33 @@ class UtilsTest(test_utils.BaseTestCase):
'other': 'value'
})
+ def test_args_array_to_list_of_dicts(self):
+ starts = ['0 11 * * *', '"0 11 * * *"', '\'0 11 * * *\'']
+ timezones = [None, 'US/Eastern', '"US/Eastern"', '\'US/Eastern\'']
+ descs = [None, 'de sc', '"de sc"', '\'de sc\'']
+ for start, tz, desc in itertools.product(starts, timezones, descs):
+ my_args = {
+ 'time_constraints': ['name=const1;start=%s;duration=1'
+ % start],
+ 'other': 'value'
+ }
+ expected = {
+ 'time_constraints': [dict(name='const1',
+ start='0 11 * * *',
+ duration='1')],
+ 'other': 'value'
+ }
+ if tz:
+ my_args['time_constraints'][0] += ';timezone=%s' % tz
+ expected['time_constraints'][0]['timezone'] = 'US/Eastern'
+ if desc:
+ my_args['time_constraints'][0] += ';description=%s' % desc
+ expected['time_constraints'][0]['description'] = 'de sc'
+
+ cleaned = utils.args_array_to_list_of_dicts(my_args,
+ 'time_constraints')
+ self.assertEqual(expected, cleaned)
+
def test_key_with_slash_to_nested_dict(self):
my_args = {
'combination_rule/alarm_ids': ['id1', 'id2'],
diff --git a/ceilometerclient/tests/v2/test_alarms.py b/ceilometerclient/tests/v2/test_alarms.py
index edccb4b..1644f20 100644
--- a/ceilometerclient/tests/v2/test_alarms.py
+++ b/ceilometerclient/tests/v2/test_alarms.py
@@ -40,6 +40,16 @@ AN_ALARM = {u'alarm_actions': [u'http://site:8000/alarm'],
u'threshold': 200.0,
u'comparison_operator': 'gt',
},
+ u'time_constraints': [{u'name': u'cons1',
+ u'description': u'desc1',
+ u'start': u'0 11 * * *',
+ u'duration': 300,
+ u'timezone': u''},
+ {u'name': u'cons2',
+ u'description': u'desc2',
+ u'start': u'0 23 * * *',
+ u'duration': 600,
+ u'timezone': ''}],
u'timestamp': u'2013-05-09T13:41:23.085000',
u'enabled': True,
u'alarm_id': u'alarm-id',
@@ -54,6 +64,8 @@ CREATE_ALARM = copy.deepcopy(AN_ALARM)
del CREATE_ALARM['timestamp']
del CREATE_ALARM['state_timestamp']
del CREATE_ALARM['alarm_id']
+CREATE_ALARM_WITHOUT_TC = copy.deepcopy(CREATE_ALARM)
+del CREATE_ALARM_WITHOUT_TC['time_constraints']
DELTA_ALARM = {u'alarm_actions': ['url1', 'url2']}
DELTA_ALARM_RULE = {u'comparison_operator': u'lt',
u'threshold': 42.1,
@@ -61,11 +73,21 @@ DELTA_ALARM_RULE = {u'comparison_operator': u'lt',
u'query': [{u'field': u'key_name',
u'op': u'eq',
u'value': u'key_value'}]}
+DELTA_ALARM_TC = [{u'name': u'cons1',
+ u'duration': 500}]
+DELTA_ALARM['time_constraints'] = DELTA_ALARM_TC
UPDATED_ALARM = copy.deepcopy(AN_ALARM)
UPDATED_ALARM.update(DELTA_ALARM)
UPDATED_ALARM['threshold_rule'].update(DELTA_ALARM_RULE)
+DELTA_ALARM['remove_time_constraints'] = 'cons2'
+UPDATED_ALARM['time_constraints'] = [{u'name': u'cons1',
+ u'description': u'desc1',
+ u'start': u'0 11 * * *',
+ u'duration': 500,
+ u'timezone': u''}]
DELTA_ALARM['threshold_rule'] = DELTA_ALARM_RULE
UPDATE_ALARM = copy.deepcopy(UPDATED_ALARM)
+UPDATE_ALARM['remove_time_constraints'] = 'cons2'
del UPDATE_ALARM['user_id']
del UPDATE_ALARM['project_id']
del UPDATE_ALARM['name']
@@ -101,6 +123,9 @@ DELTA_LEGACY_ALARM = {u'alarm_actions': ['url1', 'url2'],
u'comparison_operator': u'lt',
u'meter_name': u'foobar',
u'threshold': 42.1}
+DELTA_LEGACY_ALARM['time_constraints'] = [{u'name': u'cons1',
+ u'duration': 500}]
+DELTA_LEGACY_ALARM['remove_time_constraints'] = 'cons2'
UPDATED_LEGACY_ALARM = copy.deepcopy(AN_LEGACY_ALARM)
UPDATED_LEGACY_ALARM.update(DELTA_LEGACY_ALARM)
UPDATE_LEGACY_ALARM = copy.deepcopy(UPDATED_LEGACY_ALARM)
@@ -348,7 +373,7 @@ class AlarmLegacyManagerTest(testtools.TestCase):
def test_create(self):
alarm = self.mgr.create(**CREATE_LEGACY_ALARM)
expect = [
- ('POST', '/v2/alarms', {}, CREATE_ALARM),
+ ('POST', '/v2/alarms', {}, CREATE_ALARM_WITHOUT_TC),
]
self.assertEqual(self.api.calls, expect)
self.assertTrue(alarm)
@@ -360,7 +385,7 @@ class AlarmLegacyManagerTest(testtools.TestCase):
del create['meter_name']
alarm = self.mgr.create(**create)
expect = [
- ('POST', '/v2/alarms', {}, CREATE_ALARM),
+ ('POST', '/v2/alarms', {}, CREATE_ALARM_WITHOUT_TC),
]
self.assertEqual(self.api.calls, expect)
self.assertTrue(alarm)
@@ -392,3 +417,41 @@ class AlarmLegacyManagerTest(testtools.TestCase):
self.assertEqual(alarm.alarm_id, 'alarm-id')
for (key, value) in six.iteritems(UPDATED_ALARM):
self.assertEqual(getattr(alarm, key), value)
+
+
+class AlarmTimeConstraintTest(testtools.TestCase):
+
+ def setUp(self):
+ super(AlarmTimeConstraintTest, self).setUp()
+ self.api = utils.FakeAPI(fixtures)
+ self.mgr = alarms.AlarmManager(self.api)
+
+ def test_add_new(self):
+ new_constraint = dict(name='cons3',
+ start='0 0 * * *',
+ duration=500)
+ kwargs = dict(time_constraints=[new_constraint])
+ self.mgr.update(alarm_id='alarm-id', **kwargs)
+ actual = self.api.calls[1][3]['time_constraints']
+ expected = AN_ALARM[u'time_constraints'] + [new_constraint]
+ self.assertEqual(expected, actual)
+
+ def test_update_existing(self):
+ updated_constraint = dict(name='cons2',
+ duration=500)
+ kwargs = dict(time_constraints=[updated_constraint])
+ self.mgr.update(alarm_id='alarm-id', **kwargs)
+ actual = self.api.calls[1][3]['time_constraints']
+ expected = [AN_ALARM[u'time_constraints'][0], dict(name='cons2',
+ description='desc2',
+ start='0 23 * * *',
+ duration=500,
+ timezone='')]
+ self.assertEqual(expected, actual)
+
+ def test_remove(self):
+ kwargs = dict(remove_time_constraints=['cons2'])
+ self.mgr.update(alarm_id='alarm-id', **kwargs)
+ actual = self.api.calls[1][3]['time_constraints']
+ expected = [AN_ALARM[u'time_constraints'][0]]
+ self.assertEqual(expected, actual)
diff --git a/ceilometerclient/tests/v2/test_shell.py b/ceilometerclient/tests/v2/test_shell.py
index 4d45a0b..37dc09e 100644
--- a/ceilometerclient/tests/v2/test_shell.py
+++ b/ceilometerclient/tests/v2/test_shell.py
@@ -158,6 +158,16 @@ class ShellAlarmCommandTest(utils.BaseTestCase):
"value": "INSTANCE_ID",
"op": "eq"}],
"comparison_operator": "gt"},
+ "time_constraints": [{"name": "cons1",
+ "description": "desc1",
+ "start": "0 11 * * *",
+ "duration": 300,
+ "timezone": ""},
+ {"name": "cons2",
+ "description": "desc2",
+ "start": "0 23 * * *",
+ "duration": 600,
+ "timezone": ""}],
"alarm_id": ALARM_ID,
"state": "insufficient data",
"insufficient_data_actions": [],
@@ -276,6 +286,36 @@ class ShellAlarmCommandTest(utils.BaseTestCase):
sys.stdout.close()
sys.stdout = orig
+ def test_alarm_create_time_constraints(self):
+ shell = base_shell.CeilometerShell()
+ argv = ['alarm-threshold-create',
+ '--name', 'cpu_high',
+ '--meter-name', 'cpu_util',
+ '--threshold', '70.0',
+ '--time-constraint',
+ 'name=cons1;start="0 11 * * *";duration=300',
+ '--time-constraint',
+ 'name=cons2;start="0 23 * * *";duration=600',
+ ]
+ _, args = shell.parse_args(argv)
+
+ orig = sys.stdout
+ sys.stdout = six.StringIO()
+ alarm = alarms.Alarm(mock.Mock(), self.ALARM)
+ self.cc.alarms.create.return_value = alarm
+
+ try:
+ ceilometer_shell.do_alarm_threshold_create(self.cc, args)
+ _, kwargs = self.cc.alarms.create.call_args
+ time_constraints = [dict(name='cons1', start='0 11 * * *',
+ duration='300'),
+ dict(name='cons2', start='0 23 * * *',
+ duration='600')]
+ self.assertEqual(time_constraints, kwargs['time_constraints'])
+ finally:
+ sys.stdout.close()
+ sys.stdout = orig
+
class ShellSampleListCommandTest(utils.BaseTestCase):
diff --git a/ceilometerclient/v2/alarms.py b/ceilometerclient/v2/alarms.py
index 42f398e..837904c 100644
--- a/ceilometerclient/v2/alarms.py
+++ b/ceilometerclient/v2/alarms.py
@@ -36,7 +36,8 @@ UPDATABLE_ATTRIBUTES = [
'threshold_rule',
'combination_rule',
]
-CREATION_ATTRIBUTES = UPDATABLE_ATTRIBUTES + ['project_id', 'user_id']
+CREATION_ATTRIBUTES = UPDATABLE_ATTRIBUTES + ['project_id', 'user_id',
+ 'time_constraints']
class Alarm(base.Resource):
@@ -111,6 +112,26 @@ class AlarmManager(base.Manager):
del kwargs['matching_metadata']
kwargs['threshold_rule']['query'] = query
+ @staticmethod
+ def _merge_time_constraints(existing_tcs, kwargs):
+ new_tcs = kwargs.get('time_constraints', [])
+ if not existing_tcs:
+ updated_tcs = new_tcs
+ else:
+ updated_tcs = [dict(tc) for tc in existing_tcs]
+ for tc in new_tcs:
+ for i, old_tc in enumerate(updated_tcs):
+ if old_tc['name'] == tc['name']: # if names match, merge
+ utils.merge_nested_dict(updated_tcs[i], tc)
+ break
+ else:
+ updated_tcs.append(tc)
+ tcs_to_remove = kwargs.get('remove_time_constraints', [])
+ for tc in updated_tcs:
+ if tc['name'] in tcs_to_remove:
+ updated_tcs.remove(tc)
+ return updated_tcs
+
def create(self, **kwargs):
self._compat_legacy_alarm_kwargs(kwargs, create=True)
new = dict((key, value) for (key, value) in kwargs.items()
@@ -120,6 +141,8 @@ class AlarmManager(base.Manager):
def update(self, alarm_id, **kwargs):
self._compat_legacy_alarm_kwargs(kwargs)
updated = self.get(alarm_id).to_dict()
+ updated['time_constraints'] = self._merge_time_constraints(
+ updated.get('time_constraints', []), kwargs)
kwargs = dict((k, v) for k, v in kwargs.items()
if k in updated and k in UPDATABLE_ATTRIBUTES)
utils.merge_nested_dict(updated, kwargs, depth=1)
diff --git a/ceilometerclient/v2/shell.py b/ceilometerclient/v2/shell.py
index 628a3d9..81fe566 100644
--- a/ceilometerclient/v2/shell.py
+++ b/ceilometerclient/v2/shell.py
@@ -176,6 +176,24 @@ def alarm_rule_formatter(alarm):
return _display_rule(alarm.type, alarm.rule)
+def _display_time_constraints(time_constraints):
+ if time_constraints:
+ return ', '.join('%(name)s at %(start)s %(timezone)s for %(duration)ss'
+ % {
+ 'name': tc['name'],
+ 'start': tc['start'],
+ 'duration': tc['duration'],
+ 'timezone': tc.get('timezone', '')
+ }
+ for tc in time_constraints)
+ else:
+ return 'None'
+
+
+def time_constraints_formatter(alarm):
+ return _display_time_constraints(alarm.time_constraints)
+
+
def _infer_type(detail):
if 'type' in detail:
return detail['type']
@@ -199,6 +217,10 @@ def alarm_change_detail_formatter(change):
detail[k]))
else:
fields.append('%s: %s' % (k, detail[k]))
+ if 'time_constraints' in detail:
+ fields.append('time_constraints: %s' %
+ _display_time_constraints(
+ detail['time_constraints']))
elif change.type == 'rule change':
for k, v in six.iteritems(detail):
if k == 'rule':
@@ -218,11 +240,13 @@ def do_alarm_list(cc, args={}):
# omit action initially to keep output width sane
# (can switch over to vertical formatting when available from CLIFF)
field_labels = ['Alarm ID', 'Name', 'State', 'Enabled', 'Continuous',
- 'Alarm condition']
+ 'Alarm condition', 'Time constraints']
fields = ['alarm_id', 'name', 'state', 'enabled', 'repeat_actions',
- 'rule']
- utils.print_list(alarms, fields, field_labels,
- formatters={'rule': alarm_rule_formatter}, sortby=0)
+ 'rule', 'time_constraints']
+ utils.print_list(
+ alarms, fields, field_labels,
+ formatters={'rule': alarm_rule_formatter,
+ 'time_constraints': time_constraints_formatter}, sortby=0)
def alarm_query_formater(alarm):
@@ -233,6 +257,17 @@ def alarm_query_formater(alarm):
return r' AND\n'.join(qs)
+def alarm_time_constraints_formatter(alarm):
+ time_constraints = []
+ for tc in alarm.time_constraints:
+ lines = []
+ for k in ['name', 'description', 'start', 'duration', 'timezone']:
+ if k in tc and tc[k]:
+ lines.append(r'%s: %s' % (k, tc[k]))
+ time_constraints.append('{' + r',\n '.join(lines) + '}')
+ return '[' + r',\n '.join(time_constraints) + ']'
+
+
def _display_alarm(alarm):
fields = ['name', 'description', 'type',
'state', 'enabled', 'alarm_id', 'user_id', 'project_id',
@@ -242,6 +277,8 @@ def _display_alarm(alarm):
data.update(alarm.rule)
if alarm.type == 'threshold':
data['query'] = alarm_query_formater(alarm)
+ if alarm.time_constraints:
+ data['time_constraints'] = alarm_time_constraints_formatter(alarm)
utils.print_dict(data, wrap=72)
@@ -287,6 +324,18 @@ def common_alarm_arguments(create=False):
metavar='<Webhook URL>', action='append', default=None,
help=('URL to invoke when state transitions to '
'insufficient_data. May be used multiple times.'))
+ @utils.arg('--time-constraint', dest='time_constraints',
+ metavar='<Time Constraint>', action='append',
+ default=None,
+ help=('Only evaluate the alarm if the time at evaluation '
+ 'is within this time constraint. Start point(s) of '
+ 'the constraint are specified with a cron expression '
+ ', whereas its duration is given in seconds. '
+ 'Can be specified multiple times for multiple '
+ 'time constraints, format is: '
+ 'name=<CONSTRAINT_NAME>;start=<CRON>;'
+ 'duration=<SECONDS>;[description=<DESCRIPTION>;'
+ '[timezone=<IANA Timezone>]]'))
@functools.wraps(func)
def _wrapped(*args, **kwargs):
return func(*args, **kwargs)
@@ -320,6 +369,7 @@ def common_alarm_arguments(create=False):
def do_alarm_create(cc, args={}):
'''Create a new alarm (Deprecated).'''
fields = dict(filter(lambda x: not (x[1] is None), vars(args).items()))
+ fields = utils.args_array_to_list_of_dicts(fields, "time_constraints")
fields = utils.args_array_to_dict(fields, "matching_metadata")
alarm = cc.alarms.create(**fields)
_display_alarm(alarm)
@@ -357,6 +407,7 @@ def do_alarm_create(cc, args={}):
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.args_array_to_list_of_dicts(fields, 'time_constraints')
fields = utils.key_with_slash_to_nested_dict(fields)
fields['type'] = 'threshold'
if 'query' in fields['threshold_rule']:
@@ -382,6 +433,7 @@ def do_alarm_threshold_create(cc, args={}):
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.args_array_to_list_of_dicts(fields, 'time_constraints')
fields = utils.key_with_slash_to_nested_dict(fields)
fields['type'] = 'combination'
alarm = cc.alarms.create(**fields)
@@ -391,6 +443,10 @@ def do_alarm_combination_create(cc, args={}):
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True,
help='ID of the alarm to update.')
@common_alarm_arguments()
+@utils.arg('--remove-time-constraint', action='append',
+ metavar='<Constraint names>',
+ dest='remove_time_constraints',
+ help='Name or list of names of the time constraints to remove.')
@utils.arg('--period', type=int, metavar='<PERIOD>',
help='Length of each period (seconds) to evaluate over.')
@utils.arg('--evaluation-periods', type=int, metavar='<COUNT>',
@@ -415,6 +471,7 @@ def do_alarm_combination_create(cc, args={}):
def do_alarm_update(cc, args={}):
'''Update an existing alarm.'''
fields = dict(filter(lambda x: not (x[1] is None), vars(args).items()))
+ fields = utils.args_array_to_list_of_dicts(fields, "time_constraints")
fields = utils.args_array_to_dict(fields, "matching_metadata")
fields.pop('alarm_id')
try:
@@ -427,6 +484,10 @@ def do_alarm_update(cc, args={}):
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True,
help='ID of the alarm to update.')
@common_alarm_arguments()
+@utils.arg('--remove-time-constraint', action='append',
+ metavar='<Constraint names>',
+ dest='remove_time_constraints',
+ help='Name or list of names of the time constraints to remove.')
@utils.arg('-m', '--meter-name', metavar='<METRIC>',
dest='threshold_rule/meter_name',
help='Metric to evaluate against.')
@@ -457,6 +518,7 @@ def do_alarm_update(cc, args={}):
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.args_array_to_list_of_dicts(fields, 'time_constraints')
fields = utils.key_with_slash_to_nested_dict(fields)
fields.pop('alarm_id')
fields['type'] = 'threshold'
@@ -473,6 +535,10 @@ def do_alarm_threshold_update(cc, args={}):
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True,
help='ID of the alarm to update.')
@common_alarm_arguments()
+@utils.arg('--remove-time-constraint', action='append',
+ metavar='<Constraint names>',
+ dest='remove_time_constraints',
+ help='Name or list of names of the time constraints to remove.')
@utils.arg('--alarm_ids', action='append', metavar='<ALARM IDS>',
dest='combination_rule/alarm_ids',
help='List of alarm id.')
@@ -487,6 +553,7 @@ def do_alarm_threshold_update(cc, args={}):
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.args_array_to_list_of_dicts(fields, 'time_constraints')
fields = utils.key_with_slash_to_nested_dict(fields)
fields.pop('alarm_id')
fields['type'] = 'combination'