deb-python-monascaclient/monascaclient/v2_0/shell.py

1419 lines
57 KiB
Python

# (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP
#
# 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 datetime
import json
import numbers
import time
from monascaclient.common import utils
import monascaclient.exc as exc
from oslo_serialization import jsonutils
from six.moves import xrange
# Alarm valid types
severity_types = ['LOW', 'MEDIUM', 'HIGH', 'CRITICAL']
state_types = ['UNDETERMINED', 'ALARM', 'OK']
enabled_types = ['True', 'true', 'False', 'false']
group_by_types = ['alarm_definition_id', 'name', 'state', 'severity',
'link', 'lifecycle_state', 'metric_name',
'dimension_name', 'dimension_value']
allowed_notification_sort_by = {'id', 'name', 'type', 'address', 'created_at', 'updated_at'}
allowed_alarm_sort_by = {'alarm_id', 'alarm_definition_id',
'alarm_definition_name', 'state', 'severity',
'lifecycle_state', 'link',
'state_updated_timestamp', 'updated_timestamp',
'created_timestamp'}
allowed_definition_sort_by = {'id', 'name', 'severity', 'updated_at', 'created_at'}
# Notification valid types
notification_types = ['EMAIL', 'WEBHOOK', 'PAGERDUTY']
@utils.arg('name', metavar='<METRIC_NAME>',
help='Name of the metric to create.')
@utils.arg('--dimensions', metavar='<KEY1=VALUE1,KEY2=VALUE2...>',
help='key value pair used to create a metric dimension. '
'This can be specified multiple times, or once with parameters '
'separated by a comma. '
'Dimensions need quoting when they contain special chars [&,(,),{,},>,<] '
'that confuse the CLI parser.',
action='append')
@utils.arg('--value-meta', metavar='<KEY1=VALUE1,KEY2=VALUE2...>',
help='key value pair for extra information about a value. '
'This can be specified multiple times, or once with parameters '
'separated by a comma. '
'value_meta need quoting when they contain special chars [&,(,),{,},>,<] '
'that confuse the CLI parser.',
action='append')
@utils.arg('--time', metavar='<UNIX_TIMESTAMP>',
default=time.time() * 1000, type=int,
help='Metric timestamp in milliseconds. Default: current timestamp.')
@utils.arg('--project-id', metavar='<CROSS_PROJECT_ID>',
help='The Project ID to create metric on behalf of. '
'Requires monitoring-delegate role in keystone.',
action='append')
@utils.arg('value', metavar='<METRIC_VALUE>',
type=float,
help='Metric value.')
def do_metric_create(mc, args):
'''Create metric.'''
fields = {}
fields['name'] = args.name
if args.dimensions:
fields['dimensions'] = utils.format_parameters(args.dimensions)
fields['timestamp'] = args.time
fields['value'] = args.value
if args.value_meta:
fields['value_meta'] = utils.format_parameters(args.value_meta)
if args.project_id:
fields['tenant_id'] = args.project_id
try:
mc.metrics.create(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
print('Successfully created metric')
@utils.arg('jsonbody', metavar='<JSON_BODY>',
type=json.loads,
help='The raw JSON body in single quotes. See api doc.')
def do_metric_create_raw(mc, args):
'''Create metric from raw json body.'''
fields = {}
fields['jsonbody'] = args.jsonbody
try:
mc.metrics.create(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
print('Successfully created metric')
@utils.arg('--dimensions', metavar='<KEY1=VALUE1,KEY2=VALUE2...>',
help='key value pair used to specify a metric dimension. '
'This can be specified multiple times, or once with parameters '
'separated by a comma. '
'Dimensions need quoting when they contain special chars [&,(,),{,},>,<] '
'that confuse the CLI parser.',
action='append')
@utils.arg('--offset', metavar='<OFFSET LOCATION>',
help='The offset used to paginate the return data.')
@utils.arg('--limit', metavar='<RETURN LIMIT>',
help='The amount of data to be returned up to the API maximum limit.')
@utils.arg('--tenant-id', metavar='<TENANT_ID>',
help="Retrieve data for the specified tenant/project id instead of "
"the tenant/project from the user's Keystone credentials.")
def do_metric_name_list(mc, args):
'''List names of metrics.'''
fields = {}
if args.dimensions:
fields['dimensions'] = utils.format_dimensions_query(args.dimensions)
if args.limit:
fields['limit'] = args.limit
if args.offset:
fields['offset'] = args.offset
if args.tenant_id:
fields['tenant_id'] = args.tenant_id
try:
metric_names = mc.metrics.list_names(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
if args.json:
print(utils.json_formatter(metric_names))
return
if isinstance(metric_names, list):
utils.print_list(metric_names, ['Name'], formatters={'Name': lambda x: x['name']})
@utils.arg('--name', metavar='<METRIC_NAME>',
help='Name of the metric to list.')
@utils.arg('--dimensions', metavar='<KEY1=VALUE1,KEY2=VALUE2...>',
help='key value pair used to specify a metric dimension. '
'This can be specified multiple times, or once with parameters '
'separated by a comma. '
'Dimensions need quoting when they contain special chars [&,(,),{,},>,<] '
'that confuse the CLI parser.',
action='append')
@utils.arg('--starttime', metavar='<UTC_START_TIME>',
help='measurements >= UTC time. format: 2014-01-01T00:00:00Z. OR Format: -120 (previous 120 minutes).')
@utils.arg('--endtime', metavar='<UTC_END_TIME>',
help='measurements <= UTC time. format: 2014-01-01T00:00:00Z.')
@utils.arg('--offset', metavar='<OFFSET LOCATION>',
help='The offset used to paginate the return data.')
@utils.arg('--limit', metavar='<RETURN LIMIT>',
help='The amount of data to be returned up to the API maximum limit.')
@utils.arg('--tenant-id', metavar='<TENANT_ID>',
help="Retrieve data for the specified tenant/project id instead of "
"the tenant/project from the user's Keystone credentials.")
def do_metric_list(mc, args):
'''List metrics for this tenant.'''
fields = {}
if args.name:
fields['name'] = args.name
if args.dimensions:
fields['dimensions'] = utils.format_dimensions_query(args.dimensions)
if args.limit:
fields['limit'] = args.limit
if args.offset:
fields['offset'] = args.offset
if args.starttime:
_translate_starttime(args)
fields['start_time'] = args.starttime
if args.endtime:
fields['end_time'] = args.endtime
if args.tenant_id:
fields['tenant_id'] = args.tenant_id
try:
metric = mc.metrics.list(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
if args.json:
print(utils.json_formatter(metric))
return
cols = ['name', 'dimensions']
formatters = {
'name': lambda x: x['name'],
'dimensions': lambda x: utils.format_dict(x['dimensions']),
}
if isinstance(metric, list):
# print the list
utils.print_list(metric, cols, formatters=formatters)
else:
# add the dictionary to a list, so print_list works
metric_list = list()
metric_list.append(metric)
utils.print_list(
metric_list,
cols,
formatters=formatters)
def format_measure_timestamp(measurements):
# returns newline separated times for the timestamp column
return '\n'.join([str(m[0]) for m in measurements])
def format_measure_value(measurements):
# returns newline separated values for the value column
return '\n'.join(['{:12.3f}'.format(m[1]) for m in measurements])
def format_value_meta(measurements):
# returns newline separated values for the value column
measure_string_list = list()
for measure in measurements:
if len(measure) < 3:
measure_string = ""
else:
meta_string_list = []
for k, v in measure[2].items():
if isinstance(v, numbers.Number):
m_str = k + ': ' + str(v)
else:
m_str = k + ': ' + v
meta_string_list.append(m_str)
measure_string = ','.join(meta_string_list)
measure_string_list.append(measure_string)
return '\n'.join(measure_string_list)
def format_statistic_timestamp(statistics, columns, name):
# returns newline separated times for the timestamp column
time_index = 0
if statistics:
time_index = columns.index(name)
time_list = list()
for timestamp in statistics:
time_list.append(str(timestamp[time_index]))
return '\n'.join(time_list)
def format_statistic_value(statistics, columns, stat_type):
# find the index for column name
stat_index = 0
if statistics:
stat_index = columns.index(stat_type)
value_list = list()
for stat in statistics:
value_str = '{:12.3f}'.format(stat[stat_index])
value_list.append(value_str)
return '\n'.join(value_list)
def format_metric_name(metrics):
# returns newline separated metric names for the column
metric_string_list = list()
for metric in metrics:
metric_name = metric['name']
metric_dimensions = metric['dimensions']
metric_string_list.append(metric_name)
# need to line up with dimensions column
rng = len(metric_dimensions)
for i in range(rng):
if i == rng - 1:
# last one
break
metric_string_list.append(" ")
return '\n'.join(metric_string_list)
def format_metric_dimensions(metrics):
# returns newline separated dimension key values for the column
metric_string_list = list()
for metric in metrics:
metric_dimensions = metric['dimensions']
for k, v in metric_dimensions.items():
if isinstance(v, numbers.Number):
d_str = k + ': ' + str(v)
else:
d_str = k + ': ' + v
metric_string_list.append(d_str)
return '\n'.join(metric_string_list)
@utils.arg('name', metavar='<METRIC_NAME>',
help='Name of the metric to list measurements.')
@utils.arg('--dimensions', metavar='<KEY1=VALUE1,KEY2=VALUE2...>',
help='key value pair used to specify a metric dimension. '
'This can be specified multiple times, or once with parameters '
'separated by a comma. '
'Dimensions need quoting when they contain special chars [&,(,),{,},>,<] '
'that confuse the CLI parser.',
action='append')
@utils.arg('starttime', metavar='<UTC_START_TIME>',
help='measurements >= UTC time. format: 2014-01-01T00:00:00Z. OR Format: -120 (previous 120 minutes).')
@utils.arg('--endtime', metavar='<UTC_END_TIME>',
help='measurements <= UTC time. format: 2014-01-01T00:00:00Z.')
@utils.arg('--offset', metavar='<OFFSET LOCATION>',
help='The offset used to paginate the return data.')
@utils.arg('--limit', metavar='<RETURN LIMIT>',
help='The amount of data to be returned up to the API maximum limit.')
@utils.arg('--merge_metrics', action='store_const',
const=True,
help='Merge multiple metrics into a single result.')
@utils.arg('--group_by', metavar='<KEY1,KEY2,...>',
help='Select which keys to use for grouping. A \'*\' groups by all keys.')
@utils.arg('--tenant-id', metavar='<TENANT_ID>',
help="Retrieve data for the specified tenant/project id instead of "
"the tenant/project from the user's Keystone credentials.")
def do_measurement_list(mc, args):
'''List measurements for the specified metric.'''
fields = {}
fields['name'] = args.name
if args.dimensions:
fields['dimensions'] = utils.format_dimensions_query(args.dimensions)
_translate_starttime(args)
fields['start_time'] = args.starttime
if args.endtime:
fields['end_time'] = args.endtime
if args.limit:
fields['limit'] = args.limit
if args.offset:
fields['offset'] = args.offset
if args.merge_metrics:
fields['merge_metrics'] = args.merge_metrics
if args.group_by:
fields['group_by'] = args.group_by
if args.tenant_id:
fields['tenant_id'] = args.tenant_id
try:
metric = mc.metrics.list_measurements(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
if args.json:
print(utils.json_formatter(metric))
return
cols = ['name', 'dimensions', 'timestamp', 'value', 'value_meta']
formatters = {
'name': lambda x: x['name'],
'dimensions': lambda x: utils.format_dict(x['dimensions']),
'timestamp': lambda x: format_measure_timestamp(x['measurements']),
'value': lambda x: format_measure_value(x['measurements']),
'value_meta': lambda x: format_value_meta(x['measurements']),
}
if isinstance(metric, list):
# print the list
utils.print_list(metric, cols, formatters=formatters)
else:
# add the dictionary to a list, so print_list works
metric_list = list()
metric_list.append(metric)
utils.print_list(
metric_list,
cols,
formatters=formatters)
@utils.arg('name', metavar='<METRIC_NAME>',
help='Name of the metric to report measurement statistics.')
@utils.arg('statistics', metavar='<STATISTICS>',
help='Statistics is one or more (separated by commas) of '
'[AVG, MIN, MAX, COUNT, SUM].')
@utils.arg('--dimensions', metavar='<KEY1=VALUE1,KEY2=VALUE2...>',
help='key value pair used to specify a metric dimension. '
'This can be specified multiple times, or once with parameters '
'separated by a comma. '
'Dimensions need quoting when they contain special chars [&,(,),{,},>,<] '
'that confuse the CLI parser.',
action='append')
@utils.arg('starttime', metavar='<UTC_START_TIME>',
help='measurements >= UTC time. format: 2014-01-01T00:00:00Z. OR Format: -120 (previous 120 minutes).')
@utils.arg('--endtime', metavar='<UTC_END_TIME>',
help='measurements <= UTC time. format: 2014-01-01T00:00:00Z.')
@utils.arg('--period', metavar='<PERIOD>',
help='number of seconds per interval (default is 300)')
@utils.arg('--offset', metavar='<OFFSET LOCATION>',
help='The offset used to paginate the return data.')
@utils.arg('--limit', metavar='<RETURN LIMIT>',
help='The amount of data to be returned up to the API maximum limit.')
@utils.arg('--merge_metrics', action='store_const',
const=True,
help='Merge multiple metrics into a single result.')
@utils.arg('--group_by', metavar='<KEY1,KEY2,...>',
help='Select which keys to use for grouping. A \'*\' groups by all keys.')
@utils.arg('--tenant-id', metavar='<TENANT_ID>',
help="Retrieve data for the specified tenant/project id instead of "
"the tenant/project from the user's Keystone credentials.")
def do_metric_statistics(mc, args):
'''List measurement statistics for the specified metric.'''
statistic_types = ['AVG', 'MIN', 'MAX', 'COUNT', 'SUM']
statlist = args.statistics.split(',')
for stat in statlist:
if stat.upper() not in statistic_types:
errmsg = ('Invalid type, not one of [' +
', '.join(statistic_types) + ']')
print(errmsg)
return
fields = {}
fields['name'] = args.name
if args.dimensions:
fields['dimensions'] = utils.format_dimensions_query(args.dimensions)
_translate_starttime(args)
fields['start_time'] = args.starttime
if args.endtime:
fields['end_time'] = args.endtime
if args.period:
fields['period'] = args.period
fields['statistics'] = args.statistics
if args.limit:
fields['limit'] = args.limit
if args.offset:
fields['offset'] = args.offset
if args.merge_metrics:
fields['merge_metrics'] = args.merge_metrics
if args.group_by:
fields['group_by'] = args.group_by
if args.tenant_id:
fields['tenant_id'] = args.tenant_id
try:
metric = mc.metrics.list_statistics(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
if args.json:
print(utils.json_formatter(metric))
return
cols = ['name', 'dimensions']
# add dynamic column names
if metric:
column_names = metric[0]['columns']
for name in column_names:
cols.append(name)
else:
# when empty set, print_list needs a col
cols.append('timestamp')
formatters = {
'name': lambda x: x['name'],
'dimensions': lambda x: utils.format_dict(x['dimensions']),
'timestamp': lambda x:
format_statistic_timestamp(x['statistics'], x['columns'],
'timestamp'),
'avg': lambda x:
format_statistic_value(x['statistics'], x['columns'], 'avg'),
'min': lambda x:
format_statistic_value(x['statistics'], x['columns'], 'min'),
'max': lambda x:
format_statistic_value(x['statistics'], x['columns'], 'max'),
'count': lambda x:
format_statistic_value(x['statistics'], x['columns'], 'count'),
'sum': lambda x:
format_statistic_value(x['statistics'], x['columns'], 'sum'),
}
if isinstance(metric, list):
# print the list
utils.print_list(metric, cols, formatters=formatters)
else:
# add the dictionary to a list, so print_list works
metric_list = list()
metric_list.append(metric)
utils.print_list(
metric_list,
cols,
formatters=formatters)
def _validate_notification_period(period, notification_type):
if notification_type != 'WEBHOOK' and period != 0:
print("Invalid period, can only be non zero for webhooks")
return False
return True
@utils.arg('name', metavar='<NOTIFICATION_NAME>',
help='Name of the notification to create.')
@utils.arg('type', metavar='<TYPE>',
help='The notification type. Type must be EMAIL, WEBHOOK, or PAGERDUTY.')
@utils.arg('address', metavar='<ADDRESS>',
help='A valid EMAIL Address, URL, or SERVICE KEY.')
@utils.arg('--period', metavar='<PERIOD>', type=int, default=0,
help='A period for the notification method. Can only be non zero with webhooks')
def do_notification_create(mc, args):
'''Create notification.'''
if args.type.upper() not in notification_types:
errmsg = ('Invalid type, not one of [' +
', '.join(notification_types) + ']')
print(errmsg)
return
fields = {}
fields['name'] = args.name
fields['type'] = args.type
fields['address'] = args.address
if args.period:
if not _validate_notification_period(args.period, args.type.upper()):
return
fields['period'] = args.period
try:
notification = mc.notifications.create(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
print(jsonutils.dumps(notification, indent=2))
@utils.arg('id', metavar='<NOTIFICATION_ID>',
help='The ID of the notification.')
def do_notification_show(mc, args):
'''Describe the notification.'''
fields = {}
fields['notification_id'] = args.id
try:
notification = mc.notifications.get(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
if args.json:
print(utils.json_formatter(notification))
return
formatters = {
'name': utils.json_formatter,
'id': utils.json_formatter,
'type': utils.json_formatter,
'address': utils.json_formatter,
'period': utils.json_formatter,
'links': utils.format_dictlist,
}
utils.print_dict(notification, formatters=formatters)
@utils.arg('--sort-by', metavar='<SORT BY FIELDS>',
help='Fields to sort by as a comma separated list. Valid values are id, '
'name, type, address, created_at, updated_at. '
'Fields may be followed by "asc" or "desc", ex "address desc", '
'to set the direction of sorting.')
@utils.arg('--offset', metavar='<OFFSET LOCATION>',
help='The offset used to paginate the return data.')
@utils.arg('--limit', metavar='<RETURN LIMIT>',
help='The amount of data to be returned up to the API maximum limit.')
def do_notification_list(mc, args):
'''List notifications for this tenant.'''
fields = {}
if args.limit:
fields['limit'] = args.limit
if args.offset:
fields['offset'] = args.offset
if args.sort_by:
sort_by = args.sort_by.split(',')
for field in sort_by:
field_values = field.lower().split()
if len(field_values) > 2:
print("Invalid sort_by value {}".format(field))
if field_values[0] not in allowed_notification_sort_by:
print("Sort-by field name {} is not in [{}]".format(field_values[0],
allowed_notification_sort_by))
return
if len(field_values) > 1 and field_values[1] not in ['asc', 'desc']:
print("Invalid value {}, must be asc or desc".format(field_values[1]))
fields['sort_by'] = args.sort_by
try:
notification = mc.notifications.list(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
if args.json:
print(utils.json_formatter(notification))
return
cols = ['name', 'id', 'type', 'address', 'period']
formatters = {
'name': lambda x: x['name'],
'id': lambda x: x['id'],
'type': lambda x: x['type'],
'address': lambda x: x['address'],
'period': lambda x: x['period'],
}
if isinstance(notification, list):
utils.print_list(
notification,
cols,
formatters=formatters)
else:
notif_list = list()
notif_list.append(notification)
utils.print_list(notif_list, cols, formatters=formatters)
@utils.arg('id', metavar='<NOTIFICATION_ID>',
help='The ID of the notification.')
def do_notification_delete(mc, args):
'''Delete notification.'''
fields = {}
fields['notification_id'] = args.id
try:
mc.notifications.delete(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
print('Successfully deleted notification')
@utils.arg('id', metavar='<NOTIFICATION_ID>',
help='The ID of the notification.')
@utils.arg('name', metavar='<NOTIFICATION_NAME>',
help='Name of the notification.')
@utils.arg('type', metavar='<TYPE>',
help='The notification type. Type must be either EMAIL, WEBHOOK, or PAGERDUTY.')
@utils.arg('address', metavar='<ADDRESS>',
help='A valid EMAIL Address, URL, or SERVICE KEY.')
@utils.arg('period', metavar='<PERIOD>', type=int,
help='A period for the notification method. Can only be non zero with webhooks')
def do_notification_update(mc, args):
'''Update notification.'''
fields = {}
fields['notification_id'] = args.id
fields['name'] = args.name
if args.type.upper() not in notification_types:
errmsg = ('Invalid type, not one of [' +
', '.join(state_types) + ']')
print(errmsg)
return
fields['type'] = args.type
fields['address'] = args.address
if not _validate_notification_period(args.period, args.type.upper()):
return
fields['period'] = args.period
try:
notification = mc.notifications.update(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
print(jsonutils.dumps(notification, indent=2))
@utils.arg('id', metavar='<NOTIFICATION_ID>',
help='The ID of the notification.')
@utils.arg('--name', metavar='<NOTIFICATION_NAME>',
help='Name of the notification.')
@utils.arg('--type', metavar='<TYPE>',
help='The notification type. Type must be either EMAIL, WEBHOOK, or PAGERDUTY.')
@utils.arg('--address', metavar='<ADDRESS>',
help='A valid EMAIL Address, URL, or SERVICE KEY.')
@utils.arg('--period', metavar='<PERIOD>', type=int,
help='A period for the notification method. Can only be non zero with webhooks')
def do_notification_patch(mc, args):
'''Patch notification.'''
fields = {}
fields['notification_id'] = args.id
if args.name:
fields['name'] = args.name
if args.type:
if args.type.upper() not in notification_types:
errmsg = ('Invalid type, not one of [' +
', '.join(notification_types) + ']')
print(errmsg)
return
fields['type'] = args.type
if args.address:
fields['address'] = args.address
if args.period or args.period == 0:
if args.type and not _validate_notification_period(
args.period, args.type.upper()):
return
fields['period'] = args.period
try:
notification = mc.notifications.patch(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
print(jsonutils.dumps(notification, indent=2))
def _validate_severity(severity):
if severity.upper() not in severity_types:
errmsg = ('Invalid severity, not one of [' +
', '.join(severity_types) + ']')
print(errmsg)
return False
return True
@utils.arg('name', metavar='<ALARM_DEFINITION_NAME>',
help='Name of the alarm definition to create.')
@utils.arg('--description', metavar='<DESCRIPTION>',
help='Description of the alarm.')
@utils.arg('expression', metavar='<EXPRESSION>',
help='The alarm expression to evaluate. Quoted.')
@utils.arg('--severity', metavar='<SEVERITY>',
help='Severity is one of [LOW, MEDIUM, HIGH, CRITICAL].')
@utils.arg('--match-by', metavar='<MATCH_BY_DIMENSION_KEY1,MATCH_BY_DIMENSION_KEY2,'
'...>',
help='The metric dimensions to use to create unique alarms. '
'One or more dimension key names separated by a comma. '
'Key names need quoting when they contain special chars [&,(,),{,},>,<] '
'that confuse the CLI parser.')
@utils.arg('--alarm-actions', metavar='<NOTIFICATION-ID>',
help='The notification method to use when an alarm state is ALARM. '
'This param may be specified multiple times.',
action='append')
@utils.arg('--ok-actions', metavar='<NOTIFICATION-ID>',
help='The notification method to use when an alarm state is OK. '
'This param may be specified multiple times.',
action='append')
@utils.arg('--undetermined-actions', metavar='<NOTIFICATION-ID>',
help='The notification method to use when an alarm state is '
'UNDETERMINED. This param may be specified multiple times.',
action='append')
def do_alarm_definition_create(mc, args):
'''Create an alarm definition.'''
fields = {}
fields['name'] = args.name
if args.description:
fields['description'] = args.description
fields['expression'] = args.expression
if args.alarm_actions:
fields['alarm_actions'] = args.alarm_actions
if args.ok_actions:
fields['ok_actions'] = args.ok_actions
if args.undetermined_actions:
fields['undetermined_actions'] = args.undetermined_actions
if args.severity:
if not _validate_severity(args.severity):
return
fields['severity'] = args.severity
if args.match_by:
fields['match_by'] = args.match_by.split(',')
try:
alarm = mc.alarm_definitions.create(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
print(jsonutils.dumps(alarm, indent=2))
@utils.arg('id', metavar='<ALARM_DEFINITION_ID>',
help='The ID of the alarm definition.')
def do_alarm_definition_show(mc, args):
'''Describe the alarm definition.'''
fields = {}
fields['alarm_id'] = args.id
try:
alarm = mc.alarm_definitions.get(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
if args.json:
print(utils.json_formatter(alarm))
return
# print out detail of a single alarm
formatters = {
'name': utils.json_formatter,
'id': utils.json_formatter,
'expression': utils.json_formatter,
'expression_data': utils.format_expression_data,
'match_by': utils.json_formatter,
'actions_enabled': utils.json_formatter,
'alarm_actions': utils.json_formatter,
'ok_actions': utils.json_formatter,
'severity': utils.json_formatter,
'undetermined_actions': utils.json_formatter,
'description': utils.json_formatter,
'links': utils.format_dictlist,
}
utils.print_dict(alarm, formatters=formatters)
@utils.arg('--name', metavar='<ALARM_DEFINITION_NAME>',
help='Name of the alarm definition.')
@utils.arg('--dimensions', metavar='<KEY1=VALUE1,KEY2=VALUE2...>',
help='key value pair used to specify a metric dimension. '
'This can be specified multiple times, or once with parameters '
'separated by a comma. '
'Dimensions need quoting when they contain special chars [&,(,),{,},>,<] '
'that confuse the CLI parser.',
action='append')
@utils.arg('--severity', metavar='<SEVERITY>',
help='Severity is one of ["LOW", "MEDIUM", "HIGH", "CRITICAL"].')
@utils.arg('--sort-by', metavar='<SORT BY FIELDS>',
help='Fields to sort by as a comma separated list. Valid values are id, '
'name, severity, created_at, updated_at. '
'Fields may be followed by "asc" or "desc", ex "severity desc", '
'to set the direction of sorting.')
@utils.arg('--offset', metavar='<OFFSET LOCATION>',
help='The offset used to paginate the return data.')
@utils.arg('--limit', metavar='<RETURN LIMIT>',
help='The amount of data to be returned up to the API maximum limit.')
def do_alarm_definition_list(mc, args):
'''List alarm definitions for this tenant.'''
fields = {}
if args.name:
fields['name'] = args.name
if args.dimensions:
fields['dimensions'] = utils.format_dimensions_query(args.dimensions)
if args.severity:
if not _validate_severity(args.severity):
return
fields['severity'] = args.severity
if args.sort_by:
sort_by = args.sort_by.split(',')
for field in sort_by:
field_values = field.split()
if len(field_values) > 2:
print("Invalid sort_by value {}".format(field))
if field_values[0] not in allowed_definition_sort_by:
print("Sort-by field name {} is not in [{}]".format(field_values[0], allowed_definition_sort_by))
return
if len(field_values) > 1 and field_values[1] not in ['asc', 'desc']:
print("Invalid value {}, must be asc or desc".format(field_values[1]))
fields['sort_by'] = args.sort_by
if args.limit:
fields['limit'] = args.limit
if args.offset:
fields['offset'] = args.offset
try:
alarm = mc.alarm_definitions.list(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
if args.json:
print(utils.json_formatter(alarm))
return
cols = ['name', 'id', 'expression', 'match_by', 'actions_enabled']
formatters = {
'name': lambda x: x['name'],
'id': lambda x: x['id'],
'expression': lambda x: x['expression'],
'match_by': lambda x: utils.format_list(x['match_by']),
'actions_enabled': lambda x: x['actions_enabled'],
}
if isinstance(alarm, list):
# print the list
utils.print_list(alarm, cols, formatters=formatters)
else:
# add the dictionary to a list, so print_list works
alarm_list = list()
alarm_list.append(alarm)
utils.print_list(alarm_list, cols, formatters=formatters)
@utils.arg('id', metavar='<ALARM_DEFINITION_ID>',
help='The ID of the alarm definition.')
def do_alarm_definition_delete(mc, args):
'''Delete the alarm definition.'''
fields = {}
fields['alarm_id'] = args.id
try:
mc.alarm_definitions.delete(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
print('Successfully deleted alarm definition')
@utils.arg('id', metavar='<ALARM_DEFINITION_ID>',
help='The ID of the alarm definition.')
@utils.arg('name', metavar='<ALARM_DEFINITION_NAME>',
help='Name of the alarm definition.')
@utils.arg('description', metavar='<DESCRIPTION>',
help='Description of the alarm.')
@utils.arg('expression', metavar='<EXPRESSION>',
help='The alarm expression to evaluate. Quoted.')
@utils.arg('alarm_actions', metavar='<ALARM-NOTIFICATION-ID1,ALARM-NOTIFICATION-ID2,...>',
help='The notification method(s) to use when an alarm state is ALARM '
'as a comma separated list.')
@utils.arg('ok_actions', metavar='<OK-NOTIFICATION-ID1,OK-NOTIFICATION-ID2,...>',
help='The notification method(s) to use when an alarm state is OK '
'as a comma separated list.')
@utils.arg('undetermined_actions',
metavar='<UNDETERMINED-NOTIFICATION-ID1,UNDETERMINED-NOTIFICATION-ID2,...>',
help='The notification method(s) to use when an alarm state is UNDETERMINED '
'as a comma separated list.')
@utils.arg('actions_enabled', metavar='<ACTIONS-ENABLED>',
help='The actions-enabled boolean is one of [true,false]')
@utils.arg('match_by', metavar='<MATCH_BY_DIMENSION_KEY1,MATCH_BY_DIMENSION_KEY2,...>',
help='The metric dimensions to use to create unique alarms. '
'One or more dimension key names separated by a comma. '
'Key names need quoting when they contain special chars [&,(,),{,},>,<] '
'that confuse the CLI parser.')
@utils.arg('severity', metavar='<SEVERITY>',
help='Severity is one of [LOW, MEDIUM, HIGH, CRITICAL].')
def do_alarm_definition_update(mc, args):
'''Update the alarm definition.'''
fields = {}
fields['alarm_id'] = args.id
fields['name'] = args.name
fields['description'] = args.description
fields['expression'] = args.expression
fields['alarm_actions'] = _arg_split_patch_update(args.alarm_actions)
fields['ok_actions'] = _arg_split_patch_update(args.ok_actions)
fields['undetermined_actions'] = _arg_split_patch_update(args.undetermined_actions)
if args.actions_enabled not in enabled_types:
errmsg = ('Invalid value, not one of [' +
', '.join(enabled_types) + ']')
print(errmsg)
return
fields['actions_enabled'] = args.actions_enabled in ['true', 'True']
fields['match_by'] = _arg_split_patch_update(args.match_by)
if not _validate_severity(args.severity):
return
fields['severity'] = args.severity
try:
alarm = mc.alarm_definitions.update(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
print(jsonutils.dumps(alarm, indent=2))
@utils.arg('id', metavar='<ALARM_DEFINITION_ID>',
help='The ID of the alarm definition.')
@utils.arg('--name', metavar='<ALARM_DEFINITION_NAME>',
help='Name of the alarm definition.')
@utils.arg('--description', metavar='<DESCRIPTION>',
help='Description of the alarm.')
@utils.arg('--expression', metavar='<EXPRESSION>',
help='The alarm expression to evaluate. Quoted.')
@utils.arg('--alarm-actions', metavar='<NOTIFICATION-ID>',
help='The notification method to use when an alarm state is ALARM. '
'This param may be specified multiple times.',
action='append')
@utils.arg('--ok-actions', metavar='<NOTIFICATION-ID>',
help='The notification method to use when an alarm state is OK. '
'This param may be specified multiple times.',
action='append')
@utils.arg('--undetermined-actions', metavar='<NOTIFICATION-ID>',
help='The notification method to use when an alarm state is '
'UNDETERMINED. This param may be specified multiple times.',
action='append')
@utils.arg('--actions-enabled', metavar='<ACTIONS-ENABLED>',
help='The actions-enabled boolean is one of [true,false].')
@utils.arg('--severity', metavar='<SEVERITY>',
help='Severity is one of [LOW, MEDIUM, HIGH, CRITICAL].')
def do_alarm_definition_patch(mc, args):
'''Patch the alarm definition.'''
fields = {}
fields['alarm_id'] = args.id
if args.name:
fields['name'] = args.name
if args.description:
fields['description'] = args.description
if args.expression:
fields['expression'] = args.expression
if args.alarm_actions:
fields['alarm_actions'] = _arg_split_patch_update(args.alarm_actions, patch=True)
if args.ok_actions:
fields['ok_actions'] = _arg_split_patch_update(args.ok_actions, patch=True)
if args.undetermined_actions:
fields['undetermined_actions'] = _arg_split_patch_update(args.undetermined_actions, patch=True)
if args.actions_enabled:
if args.actions_enabled not in enabled_types:
errmsg = ('Invalid value, not one of [' +
', '.join(enabled_types) + ']')
print(errmsg)
return
fields['actions_enabled'] = args.actions_enabled in ['true', 'True']
if args.severity:
if not _validate_severity(args.severity):
return
fields['severity'] = args.severity
try:
alarm = mc.alarm_definitions.patch(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
print(jsonutils.dumps(alarm, indent=2))
@utils.arg('--alarm-definition-id', metavar='<ALARM_DEFINITION_ID>',
help='The ID of the alarm definition.')
@utils.arg('--metric-name', metavar='<METRIC_NAME>',
help='Name of the metric.')
@utils.arg('--metric-dimensions', metavar='<KEY1=VALUE1,KEY2=VALUE2...>',
help='key value pair used to specify a metric dimension. '
'This can be specified multiple times, or once with parameters '
'separated by a comma. '
'Dimensions need quoting when they contain special chars [&,(,),{,},>,<] '
'that confuse the CLI parser.',
action='append')
@utils.arg('--state', metavar='<ALARM_STATE>',
help='ALARM_STATE is one of [UNDETERMINED, OK, ALARM].')
@utils.arg('--severity', metavar='<SEVERITY>',
help='Severity is one of ["LOW", "MEDIUM", "HIGH", "CRITICAL"].')
@utils.arg('--state-updated-start-time', metavar='<UTC_STATE_UPDATED_START>',
help='Return all alarms whose state was updated on or after the time specified.')
@utils.arg('--lifecycle-state', metavar='<LIFECYCLE_STATE>',
help='The lifecycle state of the alarm.')
@utils.arg('--link', metavar='<LINK>',
help='The link to external data associated with the alarm.')
@utils.arg('--sort-by', metavar='<SORT BY FIELDS>',
help='Fields to sort by as a comma separated list. Valid values are alarm_id, '
'alarm_definition_id, state, severity, lifecycle_state, link, '
'state_updated_timestamp, updated_timestamp, created_timestamp. '
'Fields may be followed by "asc" or "desc", ex "severity desc", '
'to set the direction of sorting.')
@utils.arg('--offset', metavar='<OFFSET LOCATION>',
help='The offset used to paginate the return data.')
@utils.arg('--limit', metavar='<RETURN LIMIT>',
help='The amount of data to be returned up to the API maximum limit.')
def do_alarm_list(mc, args):
'''List alarms for this tenant.'''
fields = {}
if args.alarm_definition_id:
fields['alarm_definition_id'] = args.alarm_definition_id
if args.metric_name:
fields['metric_name'] = args.metric_name
if args.metric_dimensions:
fields['metric_dimensions'] = utils.format_dimensions_query(args.metric_dimensions)
if args.state:
if args.state.upper() not in state_types:
errmsg = ('Invalid state, not one of [' +
', '.join(state_types) + ']')
print(errmsg)
return
fields['state'] = args.state
if args.severity:
if not _validate_severity(args.severity):
return
fields['severity'] = args.severity
if args.state_updated_start_time:
fields['state_updated_start_time'] = args.state_updated_start_time
if args.lifecycle_state:
fields['lifecycle_state'] = args.lifecycle_state
if args.link:
fields['link'] = args.link
if args.limit:
fields['limit'] = args.limit
if args.offset:
fields['offset'] = args.offset
if args.sort_by:
sort_by = args.sort_by.split(',')
for field in sort_by:
field_values = field.lower().split()
if len(field_values) > 2:
print("Invalid sort_by value {}".format(field))
if field_values[0] not in allowed_alarm_sort_by:
print("Sort-by field name {} is not in [{}]".format(field_values[0], allowed_alarm_sort_by))
return
if len(field_values) > 1 and field_values[1] not in ['asc', 'desc']:
print("Invalid value {}, must be asc or desc".format(field_values[1]))
fields['sort_by'] = args.sort_by
try:
alarm = mc.alarms.list(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
if args.json:
print(utils.json_formatter(alarm))
return
cols = ['id', 'alarm_definition_id', 'alarm_definition_name', 'metric_name', 'metric_dimensions',
'severity', 'state', 'lifecycle_state', 'link', 'state_updated_timestamp',
'updated_timestamp', "created_timestamp"]
formatters = {
'id': lambda x: x['id'],
'alarm_definition_id': lambda x: x['alarm_definition']['id'],
'alarm_definition_name': lambda x: x['alarm_definition']['name'],
'metric_name': lambda x: format_metric_name(x['metrics']),
'metric_dimensions': lambda x: format_metric_dimensions(x['metrics']),
'severity': lambda x: x['alarm_definition']['severity'],
'state': lambda x: x['state'],
'lifecycle_state': lambda x: x['lifecycle_state'],
'link': lambda x: x['link'],
'state_updated_timestamp': lambda x: x['state_updated_timestamp'],
'updated_timestamp': lambda x: x['updated_timestamp'],
'created_timestamp': lambda x: x['created_timestamp'],
}
if isinstance(alarm, list):
# print the list
utils.print_list(alarm, cols, formatters=formatters)
else:
# add the dictionary to a list, so print_list works
alarm_list = list()
alarm_list.append(alarm)
utils.print_list(alarm_list, cols, formatters=formatters)
@utils.arg('id', metavar='<ALARM_ID>',
help='The ID of the alarm.')
def do_alarm_show(mc, args):
'''Describe the alarm.'''
fields = {}
fields['alarm_id'] = args.id
try:
alarm = mc.alarms.get(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
if args.json:
print(utils.json_formatter(alarm))
return
# print out detail of a single alarm
formatters = {
'id': utils.json_formatter,
'alarm_definition': utils.json_formatter,
'metrics': utils.json_formatter,
'state': utils.json_formatter,
'links': utils.format_dictlist,
}
utils.print_dict(alarm, formatters=formatters)
@utils.arg('id', metavar='<ALARM_ID>',
help='The ID of the alarm.')
@utils.arg('state', metavar='<ALARM_STATE>',
help='ALARM_STATE is one of [UNDETERMINED, OK, ALARM].')
@utils.arg('lifecycle_state', metavar='<LIFECYCLE_STATE>',
help='The lifecycle state of the alarm.')
@utils.arg('link', metavar='<LINK>',
help='A link to an external resource with information about the alarm.')
def do_alarm_update(mc, args):
'''Update the alarm state.'''
fields = {}
fields['alarm_id'] = args.id
if args.state.upper() not in state_types:
errmsg = ('Invalid state, not one of [' +
', '.join(state_types) + ']')
print(errmsg)
return
fields['state'] = args.state
fields['lifecycle_state'] = args.lifecycle_state
fields['link'] = args.link
try:
alarm = mc.alarms.update(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
print(jsonutils.dumps(alarm, indent=2))
@utils.arg('id', metavar='<ALARM_ID>',
help='The ID of the alarm.')
@utils.arg('--state', metavar='<ALARM_STATE>',
help='ALARM_STATE is one of [UNDETERMINED, OK, ALARM].')
@utils.arg('--lifecycle-state', metavar='<LIFECYCLE_STATE>',
help='The lifecycle state of the alarm.')
@utils.arg('--link', metavar='<LINK>',
help='A link to an external resource with information about the alarm.')
def do_alarm_patch(mc, args):
'''Patch the alarm state.'''
fields = {}
fields['alarm_id'] = args.id
if args.state:
if args.state.upper() not in state_types:
errmsg = ('Invalid state, not one of [' +
', '.join(state_types) + ']')
print(errmsg)
return
fields['state'] = args.state
if args.lifecycle_state:
fields['lifecycle_state'] = args.lifecycle_state
if args.link:
fields['link'] = args.link
try:
alarm = mc.alarms.patch(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
print(jsonutils.dumps(alarm, indent=2))
@utils.arg('id', metavar='<ALARM_ID>',
help='The ID of the alarm.')
def do_alarm_delete(mc, args):
'''Delete the alarm.'''
fields = {}
fields['alarm_id'] = args.id
try:
mc.alarms.delete(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
print('Successfully deleted alarm')
def output_alarm_history(args, alarm_history):
if args.json:
print(utils.json_formatter(alarm_history))
return
# format output
cols = ['alarm_id', 'new_state', 'old_state', 'reason',
'reason_data', 'metric_name', 'metric_dimensions', 'timestamp']
formatters = {
'alarm_id': lambda x: x['alarm_id'],
'new_state': lambda x: x['new_state'],
'old_state': lambda x: x['old_state'],
'reason': lambda x: x['reason'],
'reason_data': lambda x: x['reason_data'],
'metric_name': lambda x: format_metric_name(x['metrics']),
'metric_dimensions': lambda x: format_metric_dimensions(x['metrics']),
'timestamp': lambda x: x['timestamp'],
}
if isinstance(alarm_history, list):
# print the list
utils.print_list(alarm_history, cols, formatters=formatters)
else:
# add the dictionary to a list, so print_list works
alarm_list = list()
alarm_list.append(alarm_history)
utils.print_list(alarm_list, cols, formatters=formatters)
@utils.arg('--alarm-definition-id', metavar='<ALARM_DEFINITION_ID>',
help='The ID of the alarm definition.')
@utils.arg('--metric-name', metavar='<METRIC_NAME>',
help='Name of the metric.')
@utils.arg('--metric-dimensions', metavar='<KEY1=VALUE1,KEY2=VALUE2...>',
help='key value pair used to specify a metric dimension. '
'This can be specified multiple times, or once with parameters '
'separated by a comma. '
'Dimensions need quoting when they contain special chars [&,(,),{,},>,<] '
'that confuse the CLI parser.',
action='append')
@utils.arg('--state', metavar='<ALARM_STATE>',
help='ALARM_STATE is one of [UNDETERMINED, OK, ALARM].')
@utils.arg('--severity', metavar='<SEVERITY>',
help='Severity is one of ["LOW", "MEDIUM", "HIGH", "CRITICAL"].')
@utils.arg('--lifecycle-state', metavar='<LIFECYCLE_STATE>',
help='The lifecycle state of the alarm.')
@utils.arg('--link', metavar='<LINK>',
help='The link to external data associated with the alarm.')
@utils.arg('--group-by', metavar='<GROUP_BY>',
help='Comma separated list of one or more fields to group the results by. '
'Group by is one or more of [alarm_definition_id, name, state, link, '
'lifecycle_state, metric_name, dimension_name, dimension_value].')
@utils.arg('--offset', metavar='<OFFSET LOCATION>',
help='The offset used to paginate the return data.')
@utils.arg('--limit', metavar='<RETURN LIMIT>',
help='The amount of data to be returned up to the API maximum limit.')
def do_alarm_count(mc, args):
'''Count alarms.'''
fields = {}
if args.alarm_definition_id:
fields['alarm_definition_id'] = args.alarm_definition_id
if args.metric_name:
fields['metric_name'] = args.metric_name
if args.metric_dimensions:
fields['metric_dimensions'] = utils.format_parameters(args.metric_dimensions)
if args.state:
if args.state.upper() not in state_types:
errmsg = ('Invalid state, not one of [' +
', '.join(state_types) + ']')
print(errmsg)
return
fields['state'] = args.state
if args.severity:
if not _validate_severity(args.severity):
return
fields['severity'] = args.severity
if args.lifecycle_state:
fields['lifecycle_state'] = args.lifecycle_state
if args.link:
fields['link'] = args.link
if args.group_by:
group_by = args.group_by.split(',')
if not set(group_by).issubset(set(group_by_types)):
errmsg = ('Invalid group-by, one or more values not in [' +
','.join(group_by_types) + ']')
print(errmsg)
return
fields['group_by'] = args.group_by
if args.limit:
fields['limit'] = args.limit
if args.offset:
fields['offset'] = args.offset
try:
counts = mc.alarms.count(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
if args.json:
print(utils.json_formatter(counts))
return
cols = counts['columns']
utils.print_list(counts['counts'], [i for i in xrange(len(cols))],
field_labels=cols)
@utils.arg('id', metavar='<ALARM_ID>',
help='The ID of the alarm.')
@utils.arg('--offset', metavar='<OFFSET LOCATION>',
help='The offset used to paginate the return data.')
@utils.arg('--limit', metavar='<RETURN LIMIT>',
help='The amount of data to be returned up to the API maximum limit.')
def do_alarm_history(mc, args):
'''Alarm state transition history.'''
fields = {}
fields['alarm_id'] = args.id
if args.limit:
fields['limit'] = args.limit
if args.offset:
fields['offset'] = args.offset
try:
alarm = mc.alarms.history(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
output_alarm_history(args, alarm)
@utils.arg('--dimensions', metavar='<KEY1=VALUE1,KEY2=VALUE2...>',
help='key value pair used to specify a metric dimension. '
'This can be specified multiple times, or once with parameters '
'separated by a comma. '
'Dimensions need quoting when they contain special chars [&,(,),{,},>,<] '
'that confuse the CLI parser.',
action='append')
@utils.arg('--starttime', metavar='<UTC_START_TIME>',
help='measurements >= UTC time. format: 2014-01-01T00:00:00Z. OR format: -120 (previous 120 minutes).')
@utils.arg('--endtime', metavar='<UTC_END_TIME>',
help='measurements <= UTC time. format: 2014-01-01T00:00:00Z.')
@utils.arg('--offset', metavar='<OFFSET LOCATION>',
help='The offset used to paginate the return data.')
@utils.arg('--limit', metavar='<RETURN LIMIT>',
help='The amount of data to be returned up to the API maximum limit.')
def do_alarm_history_list(mc, args):
'''List alarms state history.'''
fields = {}
if args.dimensions:
fields['dimensions'] = utils.format_parameters(args.dimensions)
if args.starttime:
_translate_starttime(args)
fields['start_time'] = args.starttime
if args.endtime:
fields['end_time'] = args.endtime
if args.limit:
fields['limit'] = args.limit
if args.offset:
fields['offset'] = args.offset
try:
alarm = mc.alarms.history_list(**fields)
except exc.HTTPException as he:
raise exc.CommandError(
'HTTPException code=%s message=%s' %
(he.code, he.message))
else:
output_alarm_history(args, alarm)
def _translate_starttime(args):
if args.starttime[0] == '-':
deltaT = time.time() + (int(args.starttime) * 60)
utc = str(datetime.datetime.utcfromtimestamp(deltaT))
utc = utc.replace(" ", "T")[:-7] + 'Z'
args.starttime = utc
def _arg_split_patch_update(arg, patch=False):
if patch:
arg = ','.join(arg)
if not arg or arg == "[]":
arg_split = []
else:
arg_split = arg.split(',')
return arg_split