Added method for creating array of samples to samples manager. Added shell command for creating array of samples. Added tests for this changes. It is made for Rally tests that use big data arrays. So now only one function may be called instead of numerous calls simple create function. Change-Id: I01ba4f0c0db40c95ef72dadaa34b3fafc034e417
1190 lines
52 KiB
Python
1190 lines
52 KiB
Python
#
|
|
# Copyright 2013 Red Hat, Inc
|
|
# Copyright Ericsson AB 2014. All rights reserved
|
|
#
|
|
# Authors: Angus Salkeld <asalkeld@redhat.com>
|
|
# Balazs Gibizer <balazs.gibizer@ericsson.com>
|
|
# Ildiko Vancsa <ildiko.vancsa@ericsson.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.
|
|
|
|
import argparse
|
|
import functools
|
|
import json
|
|
|
|
from oslo_serialization import jsonutils
|
|
from oslo_utils import strutils
|
|
import six
|
|
|
|
from ceilometerclient.common import utils
|
|
from ceilometerclient import exc
|
|
from ceilometerclient.v2 import options
|
|
|
|
|
|
ALARM_STATES = ['ok', 'alarm', 'insufficient data']
|
|
ALARM_SEVERITY = ['low', 'moderate', 'critical']
|
|
ALARM_OPERATORS = ['lt', 'le', 'eq', 'ne', 'ge', 'gt']
|
|
ALARM_COMBINATION_OPERATORS = ['and', 'or']
|
|
STATISTICS = ['max', 'min', 'avg', 'sum', 'count']
|
|
AGGREGATES = {'avg': 'Avg',
|
|
'count': 'Count',
|
|
'max': 'Max',
|
|
'min': 'Min',
|
|
'sum': 'Sum',
|
|
'stddev': 'Standard deviation',
|
|
'cardinality': 'Cardinality'}
|
|
OPERATORS_STRING = dict(gt='>', ge='>=',
|
|
lt='<', le="<=",
|
|
eq='==', ne='!=')
|
|
ORDER_DIRECTIONS = ['asc', 'desc']
|
|
COMPLEX_OPERATORS = ['and', 'or']
|
|
SIMPLE_OPERATORS = ["=", "!=", "<", "<=", '>', '>=']
|
|
|
|
|
|
class NotEmptyAction(argparse.Action):
|
|
def __call__(self, parser, namespace, values, option_string=None):
|
|
values = values or getattr(namespace, self.dest)
|
|
if not values or values.isspace():
|
|
raise exc.CommandError('%s should not be empty' % self.dest)
|
|
setattr(namespace, self.dest, values)
|
|
|
|
|
|
def obsoleted_by(new_dest):
|
|
class ObsoletedByAction(argparse.Action):
|
|
def __call__(self, parser, namespace, values, option_string=None):
|
|
old_dest = option_string or self.dest
|
|
print('%s is obsolete! See help for more details.' % old_dest)
|
|
setattr(namespace, new_dest, values)
|
|
return ObsoletedByAction
|
|
|
|
|
|
@utils.arg('-q', '--query', metavar='<QUERY>',
|
|
help='key[op]data_type::value; list. data_type is optional, '
|
|
'but if supplied must be string, integer, float, or boolean.')
|
|
@utils.arg('-m', '--meter', metavar='<NAME>', required=True,
|
|
action=NotEmptyAction, help='Name of meter to list statistics for.')
|
|
@utils.arg('-p', '--period', metavar='<PERIOD>',
|
|
help='Period in seconds over which to group samples.')
|
|
@utils.arg('-g', '--groupby', metavar='<FIELD>', action='append',
|
|
help='Field for group by.')
|
|
@utils.arg('-a', '--aggregate', metavar='<FUNC>[<-<PARAM>]', action='append',
|
|
default=[], help=('Function for data aggregation. '
|
|
'Available aggregates are: '
|
|
'%s.' % ", ".join(AGGREGATES.keys())))
|
|
def do_statistics(cc, args):
|
|
"""List the statistics for a meter."""
|
|
aggregates = []
|
|
for a in args.aggregate:
|
|
aggregates.append(dict(zip(('func', 'param'), a.split("<-"))))
|
|
api_args = {'meter_name': args.meter,
|
|
'q': options.cli_to_array(args.query),
|
|
'period': args.period,
|
|
'groupby': args.groupby,
|
|
'aggregates': aggregates}
|
|
try:
|
|
statistics = cc.statistics.list(**api_args)
|
|
except exc.HTTPNotFound:
|
|
raise exc.CommandError('Samples not found: %s' % args.meter)
|
|
else:
|
|
fields_display = {'duration': 'Duration',
|
|
'duration_end': 'Duration End',
|
|
'duration_start': 'Duration Start',
|
|
'period': 'Period',
|
|
'period_end': 'Period End',
|
|
'period_start': 'Period Start',
|
|
'groupby': 'Group By'}
|
|
fields_display.update(AGGREGATES)
|
|
fields = ['period', 'period_start', 'period_end']
|
|
if args.groupby:
|
|
fields.append('groupby')
|
|
if args.aggregate:
|
|
for a in aggregates:
|
|
if 'param' in a:
|
|
fields.append("%(func)s/%(param)s" % a)
|
|
else:
|
|
fields.append(a['func'])
|
|
for stat in statistics:
|
|
stat.__dict__.update(stat.aggregate)
|
|
else:
|
|
fields.extend(['max', 'min', 'avg', 'sum', 'count'])
|
|
fields.extend(['duration', 'duration_start', 'duration_end'])
|
|
cols = [fields_display.get(f, f) for f in fields]
|
|
utils.print_list(statistics, fields, cols)
|
|
|
|
|
|
@utils.arg('-q', '--query', metavar='<QUERY>',
|
|
help='key[op]data_type::value; list. data_type is optional, '
|
|
'but if supplied must be string, integer, float, or boolean.')
|
|
@utils.arg('-m', '--meter', metavar='<NAME>',
|
|
action=NotEmptyAction, help='Name of meter to show samples for.')
|
|
@utils.arg('-l', '--limit', metavar='<NUMBER>',
|
|
help='Maximum number of samples to return.')
|
|
def do_sample_list(cc, args):
|
|
"""List the samples (return OldSample objects if -m/--meter is set)."""
|
|
if not args.meter:
|
|
return _do_sample_list(cc, args)
|
|
else:
|
|
return _do_old_sample_list(cc, args)
|
|
|
|
|
|
def _do_old_sample_list(cc, args):
|
|
fields = {'meter_name': args.meter,
|
|
'q': options.cli_to_array(args.query),
|
|
'limit': args.limit}
|
|
try:
|
|
samples = cc.samples.list(**fields)
|
|
except exc.HTTPNotFound:
|
|
raise exc.CommandError('Samples not found: %s' % args.meter)
|
|
else:
|
|
field_labels = ['Resource ID', 'Name', 'Type', 'Volume', 'Unit',
|
|
'Timestamp']
|
|
fields = ['resource_id', 'counter_name', 'counter_type',
|
|
'counter_volume', 'counter_unit', 'timestamp']
|
|
utils.print_list(samples, fields, field_labels, sortby=None)
|
|
|
|
|
|
def _do_sample_list(cc, args):
|
|
fields = {
|
|
'q': options.cli_to_array(args.query),
|
|
'limit': args.limit
|
|
}
|
|
samples = cc.new_samples.list(**fields)
|
|
field_labels = ['ID', 'Resource ID', 'Name', 'Type', 'Volume', 'Unit',
|
|
'Timestamp']
|
|
fields = ['id', 'resource_id', 'meter', 'type', 'volume', 'unit',
|
|
'timestamp']
|
|
utils.print_list(samples, fields, field_labels, sortby=None)
|
|
|
|
|
|
@utils.arg('sample_id', metavar='<SAMPLE_ID>', action=NotEmptyAction,
|
|
help='ID (aka message ID) of the sample to show.')
|
|
def do_sample_show(cc, args):
|
|
'''Show an sample.'''
|
|
try:
|
|
sample = cc.new_samples.get(args.sample_id)
|
|
except exc.HTTPNotFound:
|
|
raise exc.CommandError('Sample not found: %s' % args.sample_id)
|
|
|
|
fields = ['id', 'meter', 'volume', 'type', 'unit', 'source',
|
|
'resource_id', 'user_id', 'project_id',
|
|
'timestamp', 'recorded_at', 'metadata']
|
|
data = dict((f, getattr(sample, f, '')) for f in fields)
|
|
utils.print_dict(data, wrap=72)
|
|
|
|
|
|
def _restore_shadowed_arg(shadowed, observed):
|
|
def wrapper(func):
|
|
@functools.wraps(func)
|
|
def wrapped(cc, args):
|
|
v = getattr(args, observed, None)
|
|
setattr(args, shadowed, v)
|
|
return func(cc, args)
|
|
return wrapped
|
|
return wrapper
|
|
|
|
|
|
@utils.arg('--project-id', metavar='<SAMPLE_PROJECT_ID>',
|
|
dest='sample_project_id',
|
|
help='Tenant to associate with sample '
|
|
'(only settable by admin users).')
|
|
@utils.arg('--user-id', metavar='<SAMPLE_USER_ID>',
|
|
dest='sample_user_id',
|
|
help='User to associate with sample '
|
|
'(only settable by admin users).')
|
|
@utils.arg('-r', '--resource-id', metavar='<RESOURCE_ID>', required=True,
|
|
help='ID of the resource.')
|
|
@utils.arg('-m', '--meter-name', metavar='<METER_NAME>', required=True,
|
|
action=NotEmptyAction, help='The meter name.')
|
|
@utils.arg('--meter-type', metavar='<METER_TYPE>', required=True,
|
|
help='The meter type.')
|
|
@utils.arg('--meter-unit', metavar='<METER_UNIT>', required=True,
|
|
help='The meter unit.')
|
|
@utils.arg('--sample-volume', metavar='<SAMPLE_VOLUME>', required=True,
|
|
help='The sample volume.')
|
|
@utils.arg('--resource-metadata', metavar='<RESOURCE_METADATA>',
|
|
help='Resource metadata. Provided value should be a set of '
|
|
'key-value pairs e.g. {"key":"value"}.')
|
|
@utils.arg('--timestamp', metavar='<TIMESTAMP>',
|
|
help='The sample timestamp.')
|
|
@_restore_shadowed_arg('project_id', 'sample_project_id')
|
|
@_restore_shadowed_arg('user_id', 'sample_user_id')
|
|
def do_sample_create(cc, args={}):
|
|
"""Create a sample."""
|
|
arg_to_field_mapping = {
|
|
'meter_name': 'counter_name',
|
|
'meter_unit': 'counter_unit',
|
|
'meter_type': 'counter_type',
|
|
'sample_volume': 'counter_volume',
|
|
}
|
|
fields = {}
|
|
for var in vars(args).items():
|
|
k, v = var[0], var[1]
|
|
if v is not None:
|
|
if k == 'resource_metadata':
|
|
fields[k] = json.loads(v)
|
|
else:
|
|
fields[arg_to_field_mapping.get(k, k)] = v
|
|
sample = cc.samples.create(**fields)
|
|
fields = ['counter_name', 'user_id', 'resource_id',
|
|
'timestamp', 'message_id', 'source', 'counter_unit',
|
|
'counter_volume', 'project_id', 'resource_metadata',
|
|
'counter_type']
|
|
data = dict([(f.replace('counter_', ''), getattr(sample[0], f, ''))
|
|
for f in fields])
|
|
utils.print_dict(data, wrap=72)
|
|
|
|
|
|
@utils.arg('-q', '--query', metavar='<QUERY>',
|
|
help='key[op]data_type::value; list. data_type is optional, '
|
|
'but if supplied must be string, integer, float, or boolean.')
|
|
def do_meter_list(cc, args={}):
|
|
"""List the user's meters."""
|
|
meters = cc.meters.list(q=options.cli_to_array(args.query))
|
|
field_labels = ['Name', 'Type', 'Unit', 'Resource ID', 'User ID',
|
|
'Project ID']
|
|
fields = ['name', 'type', 'unit', 'resource_id', 'user_id',
|
|
'project_id']
|
|
utils.print_list(meters, fields, field_labels,
|
|
sortby=0)
|
|
|
|
|
|
@utils.arg('samples_list', metavar='<SAMPLES_LIST>', action=NotEmptyAction,
|
|
help='Json array with samples to create.')
|
|
def do_sample_create_list(cc, args={}):
|
|
"""Create a sample list."""
|
|
sample_list_array = json.loads(args.samples_list)
|
|
samples = cc.samples.create_list(sample_list_array)
|
|
field_labels = ['Resource ID', 'Name', 'Type', 'Volume', 'Unit',
|
|
'Timestamp']
|
|
fields = ['resource_id', 'counter_name', 'counter_type',
|
|
'counter_volume', 'counter_unit', 'timestamp']
|
|
utils.print_list(samples, fields, field_labels, sortby=None)
|
|
|
|
|
|
def _display_alarm_list(alarms, sortby=None):
|
|
# omit action initially to keep output width sane
|
|
# (can switch over to vertical formatting when available from CLIFF)
|
|
field_labels = ['Alarm ID', 'Name', 'State', 'Severity', 'Enabled',
|
|
'Continuous', 'Alarm condition', 'Time constraints']
|
|
fields = ['alarm_id', 'name', 'state', 'severity', 'enabled',
|
|
'repeat_actions', 'rule', 'time_constraints']
|
|
utils.print_list(
|
|
alarms, fields, field_labels,
|
|
formatters={'rule': alarm_rule_formatter,
|
|
'time_constraints': time_constraints_formatter_brief},
|
|
sortby=sortby)
|
|
|
|
|
|
def _display_rule(type, rule):
|
|
if type == 'threshold':
|
|
return ('%(meter_name)s %(comparison_operator)s '
|
|
'%(threshold)s during %(evaluation_periods)s x %(period)ss' %
|
|
{
|
|
'meter_name': rule['meter_name'],
|
|
'threshold': rule['threshold'],
|
|
'evaluation_periods': rule['evaluation_periods'],
|
|
'period': rule['period'],
|
|
'comparison_operator': OPERATORS_STRING.get(
|
|
rule['comparison_operator'])
|
|
})
|
|
elif type == 'combination':
|
|
return ('combinated states (%(operator)s) of %(alarms)s' % {
|
|
'operator': rule['operator'].upper(),
|
|
'alarms': ", ".join(rule['alarm_ids'])})
|
|
else:
|
|
# just dump all
|
|
return "\n".join(["%s: %s" % (f, v)
|
|
for f, v in six.iteritems(rule)])
|
|
|
|
|
|
def alarm_rule_formatter(alarm):
|
|
return _display_rule(alarm.type, alarm.rule)
|
|
|
|
|
|
def _display_time_constraints_brief(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_brief(alarm):
|
|
return _display_time_constraints_brief(getattr(alarm,
|
|
'time_constraints',
|
|
None))
|
|
|
|
|
|
def _infer_type(detail):
|
|
if 'type' in detail:
|
|
return detail['type']
|
|
elif 'meter_name' in detail['rule']:
|
|
return 'threshold'
|
|
elif 'alarms' in detail['rule']:
|
|
return 'combination'
|
|
else:
|
|
return 'unknown'
|
|
|
|
|
|
def alarm_change_detail_formatter(change):
|
|
detail = json.loads(change.detail)
|
|
fields = []
|
|
if change.type == 'state transition':
|
|
fields.append('state: %s' % detail['state'])
|
|
elif change.type == 'creation' or change.type == 'deletion':
|
|
for k in ['name', 'description', 'type', 'rule']:
|
|
if k == 'rule':
|
|
fields.append('rule: %s' % _display_rule(detail['type'],
|
|
detail[k]))
|
|
else:
|
|
fields.append('%s: %s' % (k, detail[k]))
|
|
if 'time_constraints' in detail:
|
|
fields.append('time_constraints: %s' %
|
|
_display_time_constraints_brief(
|
|
detail['time_constraints']))
|
|
elif change.type == 'rule change':
|
|
for k, v in six.iteritems(detail):
|
|
if k == 'rule':
|
|
fields.append('rule: %s' % _display_rule(_infer_type(detail),
|
|
v))
|
|
else:
|
|
fields.append('%s: %s' % (k, v))
|
|
return '\n'.join(fields)
|
|
|
|
|
|
@utils.arg('-q', '--query', metavar='<QUERY>',
|
|
help='key[op]data_type::value; list. data_type is optional, '
|
|
'but if supplied must be string, integer, float, or boolean.')
|
|
def do_alarm_list(cc, args={}):
|
|
"""List the user's alarms."""
|
|
alarms = cc.alarms.list(q=options.cli_to_array(args.query))
|
|
_display_alarm_list(alarms, sortby=0)
|
|
|
|
|
|
def alarm_query_formater(alarm):
|
|
qs = []
|
|
for q in alarm.rule['query']:
|
|
qs.append('%s %s %s' % (
|
|
q['field'], OPERATORS_STRING.get(q['op']), q['value']))
|
|
return r' AND\n'.join(qs)
|
|
|
|
|
|
def time_constraints_formatter_full(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', 'severity', '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])
|
|
data.update(alarm.rule)
|
|
if alarm.type == 'threshold':
|
|
data['query'] = alarm_query_formater(alarm)
|
|
if alarm.time_constraints:
|
|
data['time_constraints'] = time_constraints_formatter_full(alarm)
|
|
utils.print_dict(data, wrap=72)
|
|
|
|
|
|
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>',
|
|
action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
|
|
dest='alarm_id_deprecated')
|
|
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?',
|
|
action=NotEmptyAction, help='ID of the alarm to show.')
|
|
def do_alarm_show(cc, args={}):
|
|
"""Show an alarm."""
|
|
alarm = cc.alarms.get(args.alarm_id)
|
|
# alarm.get actually catches the HTTPNotFound exception and turns the
|
|
# result into None if the alarm wasn't found.
|
|
if alarm is None:
|
|
raise exc.CommandError('Alarm not found: %s' % args.alarm_id)
|
|
else:
|
|
_display_alarm(alarm)
|
|
|
|
|
|
def common_alarm_arguments(create=False):
|
|
def _wrapper(func):
|
|
@utils.arg('--name', metavar='<NAME>', required=create,
|
|
help='Name of the alarm (must be unique per tenant).')
|
|
@utils.arg('--project-id', metavar='<ALARM_PROJECT_ID>',
|
|
dest='alarm_project_id',
|
|
help='Tenant to associate with alarm '
|
|
'(only settable by admin users).')
|
|
@utils.arg('--user-id', metavar='<ALARM_USER_ID>',
|
|
dest='alarm_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('--severity', metavar='<SEVERITY>',
|
|
help='Severity of the alarm, one of: '
|
|
+ str(ALARM_SEVERITY))
|
|
@utils.arg('--enabled', type=strutils.bool_from_string,
|
|
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 '
|
|
'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)
|
|
return _wrapped
|
|
return _wrapper
|
|
|
|
|
|
def common_alarm_gnocchi_arguments(rule_namespace, create=False):
|
|
def _wrapper(func):
|
|
@utils.arg('--granularity', type=int, metavar='<GRANULARITY>',
|
|
dest=rule_namespace + '/granularity',
|
|
help='Length of each period (seconds) to evaluate over.')
|
|
@utils.arg('--evaluation-periods', type=int, metavar='<COUNT>',
|
|
dest=rule_namespace + '/evaluation_periods',
|
|
help='Number of periods to evaluate over.')
|
|
@utils.arg('--aggregation-method', metavar='<AGGREATION>',
|
|
dest=rule_namespace + '/aggregation_method',
|
|
help=('Aggregation method to use, one of: ' +
|
|
str(STATISTICS) + '.'))
|
|
@utils.arg('--comparison-operator', metavar='<OPERATOR>',
|
|
dest=rule_namespace + '/comparison_operator',
|
|
help=('Operator to compare with, one of: ' +
|
|
str(ALARM_OPERATORS) + '.'))
|
|
@utils.arg('--threshold', type=float, metavar='<THRESHOLD>',
|
|
dest=rule_namespace + '/threshold',
|
|
required=create,
|
|
help='Threshold to evaluate against.')
|
|
@utils.arg('--repeat-actions', dest='repeat_actions',
|
|
metavar='{True|False}', type=strutils.bool_from_string,
|
|
default=False,
|
|
help=('True if actions should be repeatedly notified '
|
|
'while alarm remains in target state.'))
|
|
@functools.wraps(func)
|
|
def _wrapped(*args, **kwargs):
|
|
return func(*args, **kwargs)
|
|
return _wrapped
|
|
return _wrapper
|
|
|
|
|
|
def common_alarm_gnocchi_aggregation_by_metrics_arguments(create=False):
|
|
def _wrapper(func):
|
|
@utils.arg('-m', '--metrics', metavar='<METRICS>',
|
|
dest=('gnocchi_aggregation_by_metrics_threshold_rule/'
|
|
'meter_name'),
|
|
action='append', required=create,
|
|
help='Metric to evaluate against.')
|
|
@functools.wraps(func)
|
|
def _wrapped(*args, **kwargs):
|
|
return func(*args, **kwargs)
|
|
return _wrapped
|
|
return _wrapper
|
|
|
|
|
|
def common_alarm_gnocchi_aggregation_by_resources_arguments(create=False):
|
|
def _wrapper(func):
|
|
@utils.arg('-m', '--metric', metavar='<METRIC>',
|
|
dest=('gnocchi_aggregation_by_resources_threshold_rule/'
|
|
'metric'),
|
|
required=create,
|
|
help='Metric to evaluate against.')
|
|
@utils.arg('--resource-type', metavar='<RESOURCE_TYPE>',
|
|
dest=('gnocchi_aggregation_by_resources_threshold_rule/'
|
|
'resource_type'),
|
|
required=create,
|
|
help='Resource_type to evaluate against.')
|
|
@utils.arg('--query', metavar='<QUERY>',
|
|
dest=('gnocchi_aggregation_by_resources_threshold_rule/'
|
|
'query'),
|
|
required=create,
|
|
help=('Gnocchi resources search query filter'))
|
|
@functools.wraps(func)
|
|
def _wrapped(*args, **kwargs):
|
|
return func(*args, **kwargs)
|
|
return _wrapped
|
|
return _wrapper
|
|
|
|
|
|
def common_alarm_gnocchi_resources_arguments(create=False):
|
|
def _wrapper(func):
|
|
@utils.arg('-m', '--metric', metavar='<METRIC>',
|
|
dest='gnocchi_resources_threshold_rule/metric',
|
|
required=create,
|
|
help='Metric to evaluate against.')
|
|
@utils.arg('--resource-type', metavar='<RESOURCE_TYPE>',
|
|
dest='gnocchi_resources_threshold_rule/resource_type',
|
|
required=create,
|
|
help='Resource_type to evaluate against.')
|
|
@utils.arg('--resource-id', metavar='<RESOURCE_ID>',
|
|
dest='gnocchi_resources_threshold_rule/resource_id',
|
|
required=create,
|
|
help=('Resource id to evaluate against'))
|
|
@functools.wraps(func)
|
|
def _wrapped(*args, **kwargs):
|
|
return func(*args, **kwargs)
|
|
return _wrapped
|
|
return _wrapper
|
|
|
|
|
|
@common_alarm_arguments(create=True)
|
|
@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('-m', '--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>', required=True,
|
|
help='Threshold to evaluate against.')
|
|
@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.'))
|
|
@utils.arg('--repeat-actions', dest='repeat_actions',
|
|
metavar='{True|False}', type=strutils.bool_from_string,
|
|
default=False,
|
|
help=('True if actions should be repeatedly notified '
|
|
'while alarm remains in target state.'))
|
|
@_restore_shadowed_arg('project_id', 'alarm_project_id')
|
|
@_restore_shadowed_arg('user_id', 'alarm_user_id')
|
|
def do_alarm_create(cc, args={}):
|
|
"""Create a new alarm (Deprecated). Use alarm-threshold-create instead."""
|
|
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)
|
|
|
|
|
|
@common_alarm_arguments(create=True)
|
|
@common_alarm_gnocchi_arguments('gnocchi_resources_threshold_rule',
|
|
create=True)
|
|
@common_alarm_gnocchi_resources_arguments(create=True)
|
|
def do_alarm_gnocchi_resources_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'] = 'gnocchi_resources_threshold'
|
|
alarm = cc.alarms.create(**fields)
|
|
_display_alarm(alarm)
|
|
|
|
|
|
@common_alarm_arguments(create=True)
|
|
@common_alarm_gnocchi_arguments(
|
|
'gnocchi_aggregation_by_metrics_threshold_rule', create=True)
|
|
@common_alarm_gnocchi_aggregation_by_metrics_arguments(create=True)
|
|
def do_alarm_gnocchi_aggregation_by_metrics_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'] = 'gnocchi_aggregation_by_metrics_threshold'
|
|
alarm = cc.alarms.create(**fields)
|
|
_display_alarm(alarm)
|
|
|
|
|
|
@common_alarm_arguments(create=True)
|
|
@common_alarm_gnocchi_arguments(
|
|
'gnocchi_aggregation_by_resources_threshold_rule', create=True)
|
|
@common_alarm_gnocchi_aggregation_by_resources_arguments(create=True)
|
|
def do_alarm_gnocchi_aggregation_by_resources_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'] = 'gnocchi_aggregation_by_resources_threshold'
|
|
alarm = cc.alarms.create(**fields)
|
|
_display_alarm(alarm)
|
|
|
|
|
|
@common_alarm_arguments(create=True)
|
|
@utils.arg('-m', '--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='key[op]data_type::value; list. data_type is optional, '
|
|
'but if supplied must be string, integer, float, or boolean.')
|
|
@utils.arg('--repeat-actions', dest='repeat_actions',
|
|
metavar='{True|False}', type=strutils.bool_from_string,
|
|
default=False,
|
|
help=('True if actions should be repeatedly notified '
|
|
'while alarm remains in target state.'))
|
|
@_restore_shadowed_arg('project_id', 'alarm_project_id')
|
|
@_restore_shadowed_arg('user_id', 'alarm_user_id')
|
|
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']:
|
|
fields['threshold_rule']['query'] = options.cli_to_array(
|
|
fields['threshold_rule']['query'])
|
|
alarm = cc.alarms.create(**fields)
|
|
_display_alarm(alarm)
|
|
|
|
|
|
@common_alarm_arguments(create=True)
|
|
@utils.arg('--alarm_ids', action='append', metavar='<ALARM IDS>',
|
|
required=True, dest='combination_rule/alarm_ids',
|
|
help='List of alarm IDs.')
|
|
@utils.arg('--operator', metavar='<OPERATOR>',
|
|
dest='combination_rule/operator',
|
|
help='Operator to compare with, one of: ' + str(
|
|
ALARM_COMBINATION_OPERATORS) + '.')
|
|
@utils.arg('--repeat-actions', dest='repeat_actions',
|
|
metavar='{True|False}', type=strutils.bool_from_string,
|
|
default=False,
|
|
help=('True if actions should be repeatedly notified '
|
|
'while alarm remains in target state.'))
|
|
@_restore_shadowed_arg('project_id', 'alarm_project_id')
|
|
@_restore_shadowed_arg('user_id', 'alarm_user_id')
|
|
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)
|
|
_display_alarm(alarm)
|
|
|
|
|
|
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>',
|
|
action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
|
|
dest='alarm_id_deprecated')
|
|
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?',
|
|
action=NotEmptyAction, 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>',
|
|
help='Number of periods to evaluate over.')
|
|
@utils.arg('-m', '--meter-name', metavar='<METRIC>',
|
|
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>',
|
|
help='Threshold to evaluate against.')
|
|
@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.'))
|
|
@utils.arg('--repeat-actions', dest='repeat_actions',
|
|
metavar='{True|False}', type=strutils.bool_from_string,
|
|
help=('True if actions should be repeatedly notified '
|
|
'while alarm remains in target state.'))
|
|
@_restore_shadowed_arg('project_id', 'alarm_project_id')
|
|
@_restore_shadowed_arg('user_id', 'alarm_user_id')
|
|
def do_alarm_update(cc, args={}):
|
|
"""Update an existing 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")
|
|
fields.pop('alarm_id')
|
|
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>',
|
|
action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
|
|
dest='alarm_id_deprecated')
|
|
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?',
|
|
action=NotEmptyAction, 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.')
|
|
@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>',
|
|
dest='threshold_rule/threshold',
|
|
help='Threshold to evaluate against.')
|
|
@utils.arg('-q', '--query', metavar='<QUERY>',
|
|
dest='threshold_rule/query',
|
|
help='key[op]data_type::value; list. data_type is optional, '
|
|
'but if supplied must be string, integer, float, or boolean.')
|
|
@utils.arg('--repeat-actions', dest='repeat_actions',
|
|
metavar='{True|False}', type=strutils.bool_from_string,
|
|
help=('True if actions should be repeatedly notified '
|
|
'while alarm remains in target state.'))
|
|
@_restore_shadowed_arg('project_id', 'alarm_project_id')
|
|
@_restore_shadowed_arg('user_id', 'alarm_user_id')
|
|
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'
|
|
if 'threshold_rule' in fields and '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>',
|
|
action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
|
|
dest='alarm_id_deprecated')
|
|
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?',
|
|
action=NotEmptyAction, help='ID of the alarm to update.')
|
|
@common_alarm_arguments()
|
|
@common_alarm_gnocchi_arguments('gnocchi_resources_threshold_rule')
|
|
@common_alarm_gnocchi_resources_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.')
|
|
def do_alarm_gnocchi_resources_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'] = 'gnocchi_resources_threshold'
|
|
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>',
|
|
action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
|
|
dest='alarm_id_deprecated')
|
|
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?',
|
|
action=NotEmptyAction, help='ID of the alarm to update.')
|
|
@common_alarm_arguments()
|
|
@common_alarm_gnocchi_arguments(
|
|
'gnocchi_aggregation_by_metrics_threshold_rule')
|
|
@common_alarm_gnocchi_aggregation_by_metrics_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.')
|
|
def do_alarm_gnocchi_aggregation_by_metrics_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'] = 'gnocchi_aggregation_by_metrics_threshold'
|
|
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>',
|
|
action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
|
|
dest='alarm_id_deprecated')
|
|
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?',
|
|
action=NotEmptyAction, help='ID of the alarm to update.')
|
|
@common_alarm_arguments()
|
|
@common_alarm_gnocchi_arguments(
|
|
'gnocchi_aggregation_by_resources_threshold_rule')
|
|
@common_alarm_gnocchi_aggregation_by_resources_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.')
|
|
def do_alarm_gnocchi_aggregation_by_resources_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'] = 'gnocchi_aggregation_by_resources_threshold'
|
|
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>',
|
|
action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
|
|
dest='alarm_id_deprecated')
|
|
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?',
|
|
action=NotEmptyAction, 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 IDs.')
|
|
@utils.arg('--operator', metavar='<OPERATOR>',
|
|
dest='combination_rule/operator',
|
|
help='Operator to compare with, one of: ' + str(
|
|
ALARM_COMBINATION_OPERATORS) + '.')
|
|
@utils.arg('--repeat-actions', dest='repeat_actions',
|
|
metavar='{True|False}', type=strutils.bool_from_string,
|
|
help=('True if actions should be repeatedly notified '
|
|
'while alarm remains in target state.'))
|
|
@_restore_shadowed_arg('project_id', 'alarm_project_id')
|
|
@_restore_shadowed_arg('user_id', 'alarm_user_id')
|
|
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'
|
|
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>',
|
|
action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
|
|
dest='alarm_id_deprecated')
|
|
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?',
|
|
action=NotEmptyAction, help='ID of the alarm to delete.')
|
|
def do_alarm_delete(cc, args={}):
|
|
"""Delete an alarm."""
|
|
try:
|
|
cc.alarms.delete(args.alarm_id)
|
|
except exc.HTTPNotFound:
|
|
raise exc.CommandError('Alarm not found: %s' % args.alarm_id)
|
|
|
|
|
|
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>',
|
|
action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
|
|
dest='alarm_id_deprecated')
|
|
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?',
|
|
action=NotEmptyAction, 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_state_set(cc, args={}):
|
|
"""Set the state of an alarm."""
|
|
try:
|
|
state = cc.alarms.set_state(args.alarm_id, args.state)
|
|
except exc.HTTPNotFound:
|
|
raise exc.CommandError('Alarm not found: %s' % args.alarm_id)
|
|
utils.print_dict({'state': state}, wrap=72)
|
|
|
|
|
|
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>',
|
|
action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
|
|
dest='alarm_id_deprecated')
|
|
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?',
|
|
action=NotEmptyAction, help='ID of the alarm state to show.')
|
|
def do_alarm_state_get(cc, args={}):
|
|
"""Get the state of an alarm."""
|
|
try:
|
|
state = cc.alarms.get_state(args.alarm_id)
|
|
except exc.HTTPNotFound:
|
|
raise exc.CommandError('Alarm not found: %s' % args.alarm_id)
|
|
utils.print_dict({'state': state}, wrap=72)
|
|
|
|
|
|
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>',
|
|
action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS,
|
|
dest='alarm_id_deprecated')
|
|
@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?', action=NotEmptyAction,
|
|
help='ID of the alarm for which history is shown.')
|
|
@utils.arg('-q', '--query', metavar='<QUERY>',
|
|
help='key[op]data_type::value; list. data_type is optional, '
|
|
'but if supplied must be string, integer, float, or boolean.')
|
|
def do_alarm_history(cc, args={}):
|
|
"""Display the change history of an alarm."""
|
|
kwargs = dict(alarm_id=args.alarm_id,
|
|
q=options.cli_to_array(args.query))
|
|
try:
|
|
history = cc.alarms.get_history(**kwargs)
|
|
except exc.HTTPNotFound:
|
|
raise exc.CommandError('Alarm not found: %s' % args.alarm_id)
|
|
field_labels = ['Type', 'Timestamp', 'Detail']
|
|
fields = ['type', 'timestamp', 'detail']
|
|
# We're using sortby=None as the alarm history returned from the Ceilometer
|
|
# is already sorted in the "the newer state is the earlier one in the
|
|
# list". If we'll pass any field as a sortby param, it'll be sorted in the
|
|
# ASC way by the PrettyTable
|
|
utils.print_list(history, fields, field_labels,
|
|
formatters={'detail': alarm_change_detail_formatter},
|
|
sortby=None)
|
|
|
|
|
|
@utils.arg('-q', '--query', metavar='<QUERY>',
|
|
help='key[op]data_type::value; list. data_type is optional, '
|
|
'but if supplied must be string, integer, float, or boolean.')
|
|
def do_resource_list(cc, args={}):
|
|
"""List the resources."""
|
|
resources = cc.resources.list(q=options.cli_to_array(args.query))
|
|
|
|
field_labels = ['Resource ID', 'Source', 'User ID', 'Project ID']
|
|
fields = ['resource_id', 'source', 'user_id', 'project_id']
|
|
utils.print_list(resources, fields, field_labels,
|
|
sortby=1)
|
|
|
|
|
|
@utils.arg('resource_id', metavar='<RESOURCE_ID>',
|
|
action=NotEmptyAction, help='ID of the resource to show.')
|
|
def do_resource_show(cc, args={}):
|
|
"""Show the resource."""
|
|
try:
|
|
resource = cc.resources.get(args.resource_id)
|
|
except exc.HTTPNotFound:
|
|
raise exc.CommandError('Resource not found: %s' % args.resource_id)
|
|
else:
|
|
fields = ['resource_id', 'source', 'user_id',
|
|
'project_id', 'metadata']
|
|
data = dict([(f, getattr(resource, f, '')) for f in fields])
|
|
utils.print_dict(data, wrap=72)
|
|
|
|
|
|
@utils.arg('-q', '--query', metavar='<QUERY>',
|
|
help='key[op]data_type::value; list. data_type is optional, '
|
|
'but if supplied must be string, integer, float'
|
|
'or datetime.')
|
|
@utils.arg('--no-traits', dest='no_traits', action='store_true',
|
|
help='If specified, traits will not be printed.')
|
|
def do_event_list(cc, args={}):
|
|
"""List events."""
|
|
events = cc.events.list(q=options.cli_to_array(args.query))
|
|
field_labels = ['Message ID', 'Event Type', 'Generated', 'Traits']
|
|
fields = ['message_id', 'event_type', 'generated', 'traits']
|
|
if args.no_traits:
|
|
field_labels.pop()
|
|
fields.pop()
|
|
utils.print_list(events, fields, field_labels,
|
|
formatters={
|
|
'traits': utils.nested_list_of_dict_formatter(
|
|
'traits', ['name', 'type', 'value']
|
|
)},
|
|
sortby=None)
|
|
|
|
|
|
@utils.arg('message_id', metavar='<message_id>', action=NotEmptyAction,
|
|
help='The ID of the event. Should be a UUID.')
|
|
def do_event_show(cc, args={}):
|
|
"""Show a particular event."""
|
|
try:
|
|
event = cc.events.get(args.message_id)
|
|
except exc.HTTPNotFound:
|
|
raise exc.CommandError('Event not found: %s' % args.message_id)
|
|
|
|
fields = ['event_type', 'generated', 'traits', 'raw']
|
|
data = dict([(f, getattr(event, f, '')) for f in fields])
|
|
utils.print_dict(data, wrap=72)
|
|
|
|
|
|
def do_event_type_list(cc, args={}):
|
|
"""List event types."""
|
|
event_types = cc.event_types.list()
|
|
utils.print_list(event_types, ['event_type'], ['Event Type'])
|
|
|
|
|
|
@utils.arg('-e', '--event_type', metavar='<EVENT_TYPE>',
|
|
help='Type of the event for which traits will be shown.',
|
|
required=True, action=NotEmptyAction)
|
|
def do_trait_description_list(cc, args={}):
|
|
"""List trait info for an event type."""
|
|
trait_descriptions = cc.trait_descriptions.list(args.event_type)
|
|
field_labels = ['Trait Name', 'Data Type']
|
|
fields = ['name', 'type']
|
|
utils.print_list(trait_descriptions, fields, field_labels)
|
|
|
|
|
|
@utils.arg('-e', '--event_type', metavar='<EVENT_TYPE>',
|
|
help='Type of the event for which traits will listed.',
|
|
required=True, action=NotEmptyAction)
|
|
@utils.arg('-t', '--trait_name', metavar='<TRAIT_NAME>',
|
|
help='The name of the trait to list.',
|
|
required=True, action=NotEmptyAction)
|
|
def do_trait_list(cc, args={}):
|
|
"""List all traits with name <trait_name> for Event Type <event_type>."""
|
|
traits = cc.traits.list(args.event_type, args.trait_name)
|
|
field_labels = ['Trait Name', 'Value', 'Data Type']
|
|
fields = ['name', 'value', 'type']
|
|
utils.print_list(traits, fields, field_labels)
|
|
|
|
|
|
@utils.arg('-f', '--filter', metavar='<FILTER>',
|
|
help=('{complex_op: [{simple_op: {field_name: value}}]} '
|
|
'The complex_op is one of: ' + str(COMPLEX_OPERATORS) + ', '
|
|
'simple_op is one of: ' + str(SIMPLE_OPERATORS) + '.'))
|
|
@utils.arg('-o', '--orderby', metavar='<ORDERBY>',
|
|
help=('[{field_name: direction}, {field_name: direction}] '
|
|
'The direction is one of: ' + str(ORDER_DIRECTIONS) + '.'))
|
|
@utils.arg('-l', '--limit', metavar='<LIMIT>',
|
|
help='Maximum number of samples to return.')
|
|
def do_query_samples(cc, args):
|
|
"""Query samples."""
|
|
fields = {'filter': args.filter,
|
|
'orderby': args.orderby,
|
|
'limit': args.limit}
|
|
try:
|
|
samples = cc.query_samples.query(**fields)
|
|
except exc.HTTPNotFound:
|
|
raise exc.CommandError('Samples not found')
|
|
else:
|
|
field_labels = ['Resource ID', 'Meter', 'Type', 'Volume', 'Unit',
|
|
'Timestamp']
|
|
fields = ['resource_id', 'meter', 'type',
|
|
'volume', 'unit', 'timestamp']
|
|
utils.print_list(samples, fields, field_labels,
|
|
sortby=None)
|
|
|
|
|
|
@utils.arg('-f', '--filter', metavar='<FILTER>',
|
|
help=('{complex_op: [{simple_op: {field_name: value}}]} '
|
|
'The complex_op is one of: ' + str(COMPLEX_OPERATORS) + ', '
|
|
'simple_op is one of: ' + str(SIMPLE_OPERATORS) + '.'))
|
|
@utils.arg('-o', '--orderby', metavar='<ORDERBY>',
|
|
help=('[{field_name: direction}, {field_name: direction}] '
|
|
'The direction is one of: ' + str(ORDER_DIRECTIONS) + '.'))
|
|
@utils.arg('-l', '--limit', metavar='<LIMIT>',
|
|
help='Maximum number of alarms to return.')
|
|
def do_query_alarms(cc, args):
|
|
"""Query Alarms."""
|
|
fields = {'filter': args.filter,
|
|
'orderby': args.orderby,
|
|
'limit': args.limit}
|
|
try:
|
|
alarms = cc.query_alarms.query(**fields)
|
|
except exc.HTTPNotFound:
|
|
raise exc.CommandError('Alarms not found')
|
|
else:
|
|
_display_alarm_list(alarms, sortby=None)
|
|
|
|
|
|
@utils.arg('-f', '--filter', metavar='<FILTER>',
|
|
help=('{complex_op: [{simple_op: {field_name: value}}]} '
|
|
'The complex_op is one of: ' + str(COMPLEX_OPERATORS) + ', '
|
|
'simple_op is one of: ' + str(SIMPLE_OPERATORS) + '.'))
|
|
@utils.arg('-o', '--orderby', metavar='<ORDERBY>',
|
|
help=('[{field_name: direction}, {field_name: direction}] '
|
|
'The direction is one of: ' + str(ORDER_DIRECTIONS) + '.'))
|
|
@utils.arg('-l', '--limit', metavar='<LIMIT>',
|
|
help='Maximum number of alarm history items to return.')
|
|
def do_query_alarm_history(cc, args):
|
|
"""Query Alarm History."""
|
|
fields = {'filter': args.filter,
|
|
'orderby': args.orderby,
|
|
'limit': args.limit}
|
|
try:
|
|
alarm_history = cc.query_alarm_history.query(**fields)
|
|
except exc.HTTPNotFound:
|
|
raise exc.CommandError('Alarm history not found')
|
|
else:
|
|
field_labels = ['Alarm ID', 'Event ID', 'Type', 'Detail', 'Timestamp']
|
|
fields = ['alarm_id', 'event_id', 'type', 'detail', 'timestamp']
|
|
utils.print_list(alarm_history, fields, field_labels,
|
|
formatters={'rule': alarm_change_detail_formatter},
|
|
sortby=None)
|
|
|
|
|
|
def do_capabilities(cc, args):
|
|
"""Print Ceilometer capabilities."""
|
|
capabilities = cc.capabilities.get().to_dict()
|
|
# Capability is a nested dict, and has no user defined data,
|
|
# so it is safe to format here with json tools.
|
|
for key in capabilities:
|
|
# remove the leading and trailing pair of {}
|
|
capabilities[key] = jsonutils.dumps(capabilities[key],
|
|
sort_keys=True, indent=0)[2:-2]
|
|
utils.print_dict(capabilities)
|